Skip to content

feat: auditd - new role - initial commit#1

Open
richm wants to merge 1 commit intolinux-system-roles:mainfrom
richm:initial_version
Open

feat: auditd - new role - initial commit#1
richm wants to merge 1 commit intolinux-system-roles:mainfrom
richm:initial_version

Conversation

@richm
Copy link
Copy Markdown
Contributor

@richm richm commented Apr 15, 2026

This is the initial commit of the new role auditd.

Signed-off-by: Rich Megginson rmeggins@redhat.com

Summary by Sourcery

Introduce a new linux-system-roles.auditd Ansible role to manage auditd configuration and rules with comprehensive CI, docs, and testing support.

New Features:

  • Add an auditd role that installs audit packages, renders auditd.conf from variables, and optionally manages custom audit rules.
  • Provide extensive configurable variables for auditd behavior, transport, logging, and rule definitions, including rpm-ostree support.
  • Include example playbooks demonstrating basic configuration and rules purging scenarios.

Enhancements:

  • Implement robust validation of role inputs via argument_specs and custom assertion tasks aligned with auditd upstream limits.
  • Add handlers and helper tasks to safely manage auditd service restarts, rule loading, and platform-specific variable resolution.
  • Introduce an rpm-ostree helper script to derive required packages across roles for immutable systems.

CI:

  • Add GitHub Actions workflows for ansible-lint, ansible-test, qemu/container integration tests, Testing Farm runs, markdownlint, ShellCheck, codespell, woke, PR title linting, weekly CI, README HTML generation, and changelog-driven tagging and Galaxy publishing.

Documentation:

  • Create detailed README documentation for the auditd role, rpm-ostree usage, contribution guidelines, and Ansible introduction links.
  • Add Testing Farm plan documentation describing CI test execution and how to run tests locally.

Tests:

  • Add integration, validation, and idempotence tests for auditd configuration, rule management, invalid input handling, and include_vars behavior.
  • Provide snapshot/setup and helper tasks to back up and restore system state during tests.

Chores:

  • Configure markdownlint, yamllint, commitlint, Dependabot, ansible-lint, and various repo metadata files for consistent maintenance.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 15, 2026

Reviewer's Guide

Adds a new linux-system-roles.auditd Ansible role that manages auditd.conf and custom audit rules, with full CI, validation, rpm-ostree support, documentation, and tests for argument sanity, rules purging, and integration behavior.

Sequence diagram for auditd rules purge and retention behavior

sequenceDiagram
    actor Admin
    participant AnsiblePlay
    participant Role_auditd as Role_auditd
    participant Tasks_main as Tasks_main_yml
    participant Tasks_purge as Tasks_purge_rules_yml
    participant Template_custom as Template_custom_rules_j2
    participant Handler_augenrules as Handler_Run_augenrules

    Admin->>AnsiblePlay: Run play with auditd_purge_rules=true
    AnsiblePlay->>Role_auditd: Apply linux_system_roles.auditd
    Role_auditd->>Tasks_main: Execute tasks/main.yml

    Note over Tasks_main: Earlier tasks validate vars, set facts,
    Note over Tasks_main: install packages, create rules.d

    alt auditd_purge_rules and auditd_manage_rules are true
        Tasks_main->>Tasks_purge: Include purge_rules.yml
        Tasks_purge->>Tasks_purge: Find files in rules.d
        Tasks_purge->>Tasks_purge: Stat custom.rules
        opt custom.rules exists
            Tasks_purge->>Tasks_purge: Slurp custom.rules content
            Tasks_purge->>Template_custom: Lookup rendered custom.rules
            Template_custom-->>Tasks_purge: Rendered rules text
            Tasks_purge->>Tasks_purge: Compute expected_sig from rendered body
            Tasks_purge->>Tasks_purge: Compute current_sig from file body
            alt Signatures equal
                Tasks_purge->>Tasks_purge: Set __auditd_purge_keep_custom_rules=true
            else Signatures differ
                Tasks_purge->>Tasks_purge: Set __auditd_purge_keep_custom_rules=false
            end
        end
        loop For each file in rules.d
            alt File is custom.rules and __auditd_purge_keep_custom_rules is true
                Tasks_purge-->>Tasks_purge: Skip deletion
            else Delete file
                Tasks_purge->>Tasks_purge: Remove file
                Tasks_purge->>Handler_augenrules: notify Run_augenrules
            end
        end
    else auditd_purge_rules is false or manage_rules is false
        Tasks_main-->>Tasks_main: Skip purge_rules.yml
    end

    Note over Tasks_main,Template_custom: Later, if auditd_manage_rules and not __auditd_purge_keep_custom_rules
    Note over Tasks_main,Template_custom: template custom.rules.j2 overwrites custom.rules and notifies Run_augenrules

    Handler_augenrules-->>Handler_augenrules: Run augenrules
    Handler_augenrules->>Handler_augenrules: Run augenrules --load when allowed
    Handler_augenrules-->>Admin: Audit rules updated on target
Loading

Class diagram for linux_system_roles_auditd role structure

classDiagram
    class Role_linux_system_roles_auditd {
      +defaults_main_yml
      +vars_main_yml
      +tasks_main_yml
      +handlers_main_yml
      +templates_auditd_conf_j2
      +templates_custom_rules_j2
      +meta_main_yml
      +meta_argument_specs_yml
    }

    class Defaults_main_yml {
      +bool auditd_local_events
      +bool auditd_write_logs
      +string auditd_log_file
      +string auditd_log_format
      +string auditd_log_group
      +string auditd_flush
      +int auditd_freq
      +int auditd_num_logs
      +string auditd_dispatcher
      +string auditd_name_format
      +string auditd_name
      +string auditd_disp_qos
      +int auditd_max_log_file
      +string auditd_max_log_file_action
      +string auditd_max_log_file_action_exe
      +string auditd_space_left
      +string auditd_space_left_action
      +string auditd_space_left_action_exe
      +string auditd_action_mail_acct
      +bool auditd_verify_email
      +string auditd_admin_space_left
      +string auditd_admin_space_left_action
      +string auditd_admin_space_left_action_exe
      +string auditd_disk_full_action
      +string auditd_disk_full_action_exe
      +string auditd_disk_error_action
      +string auditd_disk_error_action_exe
      +int auditd_priority_boost
      +int auditd_tcp_listen_port
      +int auditd_tcp_listen_queue
      +int auditd_tcp_max_per_addr
      +bool auditd_use_libwrap
      +string auditd_tcp_client_ports
      +int auditd_tcp_client_max_idle
      +string auditd_transport
      +bool auditd_enable_krb5
      +string auditd_krb5_principal
      +string auditd_krb5_key_file
      +bool auditd_distribute_network
      +int auditd_q_depth
      +string auditd_overflow_action
      +int auditd_max_restarts
      +string auditd_plugin_dir
      +int auditd_end_of_event_timeout
      +string auditd_report_interval
      +int auditd_buffer_size
      +int auditd_fail_mode
      +int auditd_maximum_rate
      +int auditd_enable_flag
      +bool auditd_manage_rules
      +bool auditd_purge_rules
      +bool auditd_start_service
      +string auditd_default_arch
      +list auditd_rules
    }

    class Vars_main_yml {
      +map _auditd_packages_map
      +string auditd_config_directory
      +string auditd_config_file
      +string auditd_service
      +map _auditd_permissions
      +list __auditd_required_facts
      +list __auditd_required_facts_subsets
      +list __auditd_rh_distros
      +list __auditd_rh_distros_fedora
      +bool __auditd_is_rh_distro
      +bool __auditd_is_rh_distro_fedora
    }

    class Tasks_main_yml {
      +task Validate_role_parameters
      +task Include_set_vars
      +task Install_audit_packages
      +task Deploy_auditd_configuration
      +task Ensure_rulesd_directory
      +task Purge_rulesd_when_requested
      +task Deploy_custom_audit_rules
      +task Start_and_enable_auditd_service
    }

    class Tasks_set_vars_yml {
      +task Ensure_required_facts
      +task Detect_ostree
      +task Include_platform_vars_files
      +task Resolve_package_names
      +bool __auditd_is_ostree
      +list __auditd_required_facts_subsets
      +list __auditd_packages
    }

    class Tasks_assert_yml {
      +assert auditd_num_logs_range
      +assert auditd_freq_range
      +assert_incremental_flush_requires_freq
      +assert_priority_boost_range
      +assert_q_depth_range
      +assert_max_restarts_range
      +assert_tcp_ports_ranges
      +assert_tcp_client_ports_format
      +assert_space_left_action_not_halt
      +assert_disk_full_action_not_email
      +assert_disk_error_action_not_email_rotate
      +assert_exec_actions_have_paths
      +assert_name_when_user_format
      +assert_auditd_rules_structure
      +assert_syscall_rule_keys
      +assert_file_rule_permissions
    }

    class Tasks_purge_rules_yml {
      +task Discover_rulesd_files
      +task Stat_custom_rules
      +task Slurp_custom_rules
      +task Compute_expected_signature
      +task Compute_current_signature
      +task Decide_keep_custom_rules
      +task Remove_rulesd_files
      +bool __auditd_purge_keep_custom_rules
    }

    class Handlers_main_yml {
      +handler Run_augenrules
      +handler Load_audit_rules
      +handler Restart_auditd
    }

    class Template_auditd_conf_j2 {
      +macro auditd_yesno
      +render auditd_conf_from_variables
    }

    class Template_custom_rules_j2 {
      +render_global_settings
      +render_file_watch_rules
      +render_syscall_rules
      +render_generic_rules
      +use _auditd_permissions
    }

    class Meta_main_yml {
      +string namespace
      +string role_name
      +string author
      +string description
      +string company
      +string license
      +string min_ansible_version
      +list platforms
      +list galaxy_tags
    }

    Role_linux_system_roles_auditd *-- Defaults_main_yml
    Role_linux_system_roles_auditd *-- Vars_main_yml
    Role_linux_system_roles_auditd *-- Tasks_main_yml
    Role_linux_system_roles_auditd *-- Handlers_main_yml
    Role_linux_system_roles_auditd *-- Template_auditd_conf_j2
    Role_linux_system_roles_auditd *-- Template_custom_rules_j2
    Role_linux_system_roles_auditd *-- Meta_main_yml
    Tasks_main_yml --> Tasks_set_vars_yml
    Tasks_main_yml --> Tasks_assert_yml
    Tasks_main_yml --> Tasks_purge_rules_yml
    Tasks_main_yml --> Handlers_main_yml
    Tasks_purge_rules_yml --> Template_custom_rules_j2
    Template_custom_rules_j2 --> Vars_main_yml
    Template_auditd_conf_j2 --> Defaults_main_yml
    Tasks_assert_yml --> Vars_main_yml
    Tasks_set_vars_yml --> Vars_main_yml
Loading

Flow diagram for get_ostree_data_sh package resolution and output

flowchart TD
    A[Start get_ostree_data_sh
args category pkgtype distro_ver format] --> B{Args valid?}
    B -- no --> Z[Exit with_usage]
    B -- yes --> C[Set category
Set pkgtypes
Parse distro and version]

    C --> D{category == packages}
    D -- no --> Z2[Unsupported_category
exit]
    D -- yes --> E[Call get_packages
with ostree_dir]

    subgraph get_packages_recursive
        E --> F[For each pkgtype in pkgtypes]
        F --> G[For each suffix in
"" -distro -distro-major -distro-ver]
        G --> H[Check packages-pkgtype-suffix txt]
        H --> I{File exists?}
        I -- yes --> J[Output package lines]
        I -- no --> K[Skip]

        F --> L[Check roles-pkgtype txt]
        L --> M{roles file exists?}
        M -- no --> N[Skip roles]
        M -- yes --> O[Read role names]
        O --> P[For each role
call get_rolepath]
        P --> Q{rolepath found?}
        Q -- no --> R[Print error
exit 2]
        Q -- yes --> S[Recurse get_packages
on rolepath]

        J --> T[Collect all packages]
        S --> T
        T --> U[Sort -u packages]
    end

    U --> V{format}
    V -- json --> W1[format_packages_json]
    V -- yaml --> W2[format_packages_yaml]
    V -- raw --> W3[format_packages_raw]
    V -- toml --> W4[format_packages_toml]

    W1 --> X[Print JSON list]
    W2 --> X
    W3 --> X
    W4 --> X
    X --> Y[End]

    subgraph get_rolepath_resolution
        GR1[Given ostree_dir and role] --> GR2[Check rolesdir under parent collection
role_path role ostree]
        GR2 --> GR3{Exists?}
        GR3 -- yes --> GR7[Return rolesdir]
        GR3 -- no --> GR4[Check legacy role dirs
*-system-roles role
.ostree]
        GR4 --> GR5{Exists?}
        GR5 -- yes --> GR7
        GR5 -- no --> GR6[Search ANSIBLE_COLLECTIONS_PATH
for role .ostree]
        GR6 --> GR8{Found?}
        GR8 -- yes --> GR7
        GR8 -- no --> GR9[Print error
exit 2]
    end
Loading

File-Level Changes

Change Details Files
Introduce fully‑featured auditd Ansible role with config templating, rules management, handlers, and platform-specific variable resolution.
  • Define role defaults mirroring audit-userspace clear_config in defaults/main.yml, including auditd.conf options and rule controls like auditd_manage_rules, auditd_purge_rules, and auditd_rules.
  • Implement main task flow (tasks/main.yml) to validate inputs, gather required facts, resolve per‑OS packages/paths, install audit packages, render auditd.conf and rules.d/custom.rules, optionally purge rules.d, and manage the auditd service.
  • Add set_vars.yml and vars/main.yml to resolve required facts, rpm-ostree detection, OS-family package mapping, and reusable permission mappings and RH distro helpers.
  • Provide auditd.conf.j2 and custom.rules.j2 templates to render daemon config and rule fragments from role variables, including exec actions and rule types (file, syscall, generic).
  • Define handlers to regenerate and load rules with augenrules and restart auditd when configs or rules change.
defaults/main.yml
tasks/main.yml
tasks/set_vars.yml
vars/main.yml
templates/auditd.conf.j2
templates/custom.rules.j2
handlers/main.yml
Add strong input validation for auditd parameters aligned with auditd-config.c and test coverage for invalid inputs and rules semantics.
  • Create meta/argument_specs.yml to constrain types and enum choices for auditd_* variables (e.g., log_format, transport, *_action values).
  • Implement tasks/assert.yml to enforce numeric ranges, cross-field constraints (e.g., freq with incremental flush, exec actions requiring *_exe paths, rules schema and permissions), and special constraints like disallowed actions for disk_full/error and space_left_action.
  • Add tests/tests_invalid_input.yml to assert that both argument_specs and tasks/assert.yml reject bad values and malformed auditd_rules.
  • Provide tests/templates/get_ansible_managed.j2 and tasks/check_header.yml to verify ansible_managed headers and fingerprints in generated files.
meta/argument_specs.yml
tasks/assert.yml
tests/tests_invalid_input.yml
tests/tasks/check_header.yml
tests/templates/get_ansible_managed.j2
Implement purge and idempotent management of audit rules under rules.d, with tests ensuring behavior and system restoration.
  • Add purge_rules.yml to discover files under rules.d, compare existing custom.rules body (from first -D onward) with expected template, preserve matching custom.rules while deleting other fragments, and set a fact to skip rewrite.
  • Integrate purge_rules.yml into main flow when auditd_purge_rules and auditd_manage_rules are true, and conditionally skip templating when existing custom.rules is preserved.
  • Create tests/tests_purge_rules.yml plus tests/tasks/setup.yml and tests/tasks/cleanup.yml to snapshot current rules, add a decoy fragment, run the role with purge enabled, verify decoy deletion and custom.rules checksum stability across runs, then restore original state.
tasks/purge_rules.yml
tasks/main.yml
tests/tests_purge_rules.yml
tests/tasks/setup.yml
tests/tasks/cleanup.yml
Add integration, default-path, and include_vars tests plus helper taskfiles to exercise role behavior in realistic scenarios.
  • Add tests/tests_default.yml that runs the role with cleared facts and checks auditd.conf header for ansible_managed and role fingerprint.
  • Add tests/tests_auditd_integration.yml to configure non-default auditd settings and rules, then verify rendered auditd.conf and custom.rules content line-by-line before restoring backups.
  • Introduce tests/tests_include_vars_from_parent.yml and tests/roles/caller to validate that caller vars do not override role vars when include_role is used.
  • Provide tests/tasks/run_role_with_clear_facts.yml to clear facts and include the role with optional failure suppression, and tests/setup-snapshot.yml to pre-install OS packages for snapshot images.
  • Add tests/vars/rh_distros_vars.yml for RH distro helpers in tests, mirroring role vars.
tests/tests_default.yml
tests/tests_auditd_integration.yml
tests/tests_include_vars_from_parent.yml
tests/setup-snapshot.yml
tests/tasks/run_role_with_clear_facts.yml
tests/roles/caller/tasks/main.yml
tests/roles/caller/vars/main.yml
tests/vars/rh_distros_vars.yml
Add documentation and examples for the role, rpm-ostree behavior, contribution workflow, and test plans.
  • Create README.md describing role purpose, all auditd_* variables with semantics and defaults, rules format, rpm-ostree usage pointer, and example playbook.
  • Add README-ostree.md explaining package resolution and get_ostree_data.sh usage to compute package lists for various DISTRO-VERSION and output formats.
  • Add README-ansible.md linking to shared Linux System Roles Ansible introduction, contributing.md with development/testing workflow, and README-plans.md describing tmt/Testing Farm CI plans.
  • Add examples/simple.yml and examples/purge_rules.yml to demonstrate basic configuration and purge_rules behavior.
README.md
README-ostree.md
README-ansible.md
contributing.md
plans/README-plans.md
examples/simple.yml
examples/purge_rules.yml
Provide rpm-ostree support tooling and common repo metadata for CI, docs, linting, and release automation.
  • Introduce .ostree/get_ostree_data.sh plus .ostree/README.md to compute package lists (runtime/testing) with support for nested role dependencies and multiple output formats (json/yaml/raw/toml).
  • Add meta/main.yml and meta/collection-requirements.yml defining Galaxy metadata, supported platforms/tags, and ansible.posix dependency, along with minimal tox.ini to enable tox-lsr.
  • Add markdownlint, yamllint, codespell, woke, ansible-lint, ansible-test, shellcheck, docs build, release/tagging (changelog_to_tag.yml), weekly CI trigger, ostree/qemu/container integration tests, and Testing Farm workflows.
  • Add PR title linting via commitlint (.commitlintrc.js and pr-title-lint.yml), dependabot config, codespell configuration files, PR template, CODEOWNERS, docs template, and CI helpers such as ansible-managed-var-comment.yml and test_converting_readme.yml.
  • Stub .ansible-lint, .codespell_ignores, .codespellrc, .fmf/version, plans/test_playbooks_parallel.fmf, and test role directories for collection conversion/testing scaffolding.
.ostree/get_ostree_data.sh
.ostree/README.md
meta/main.yml
meta/collection-requirements.yml
tox.ini
.markdownlint.yaml
.yamllint.yml
.github/workflows/*
.commitlintrc.js
.github/dependabot.yml
.github/CODEOWNERS
.github/pull_request_template.md
.pandoc_template.html5
.ansible-lint
.codespell_ignores
.codespellrc
.fmf/version
plans/test_playbooks_parallel.fmf
tests/roles/linux-system-roles.auditd/*

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 6 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path=".github/workflows/build_docs.yml" line_range="99-103" />
<code_context>
+          name: README.html
+          path: ${{ env.RELEASE_VERSION }}/README.html
+
+      - name: Commit changes
+        run: |
+          git config --global user.name "${{ github.actor }}"
+          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
+          git add ${{ env.RELEASE_VERSION }}/README.html docs/index.html
+          git commit -m "Update README.html for ${{ env.RELEASE_VERSION }}"
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard adding docs/index.html so the workflow does not fail when RELEASE_VERSION != 'latest'

`docs/index.html` is only created when `RELEASE_VERSION == 'latest'`, but `git add ... docs/index.html` always runs. For tag-based releases, this file won’t exist and the `git add` will fail. Please either guard `git add docs/index.html` with `RELEASE_VERSION == 'latest'` or move it to a separate, conditional `git add` that matches the copy step’s condition.
</issue_to_address>

### Comment 2
<location path=".github/workflows/tft.yml" line_range="119-120" />
<code_context>
+            ansible_version: "2.17"
+    runs-on: ubuntu-latest
+    env:
+      ARTIFACTS_DIR_NAME: "tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\
+        ${{ matrix.platform }}-${{ matrix.ansible_version }}_\
+        ${{ needs.prepare_vars.outputs.datetime }}/artifacts"
+      ARTIFACT_TARGET_DIR: /srv/pub/alt/${{ vars.SR_LSR_USER }}/logs
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid referencing a non-existent datetime output from prepare_vars in ARTIFACTS_DIR_NAME

`ARTIFACTS_DIR_NAME` uses `${{ needs.prepare_vars.outputs.datetime }}`, but `prepare_vars` doesn’t define a `datetime` output. That will leave an empty segment in the path and diverges from the `Set variables with DATETIME` step, which recomputes `DATETIME` and artifact paths. Please either expose `DATETIME` as an output from `prepare_vars` and use it here, or remove this interpolation and rely on the value computed in `set_vars` only.

Suggested implementation:

```
    env:
      ARTIFACT_TARGET_DIR: /srv/pub/alt/${{ vars.SR_LSR_USER }}/logs

```

If subsequent steps rely on `ARTIFACTS_DIR_NAME` as a job-level environment variable, they should be updated to either:
1. Use the value computed in `set_vars` (e.g., by echoing it to `$GITHUB_OUTPUT` in `set_vars` and then referencing `steps.set_vars.outputs.ARTIFACTS_DIR_NAME`), or
2. Recompute the same value locally in their own `run` sections, mirroring the logic from `set_vars`.

This will ensure there is a single source of truth for the artifacts directory name and no dependency on a non-existent `prepare_vars.outputs.datetime`.
</issue_to_address>

### Comment 3
<location path=".ostree/get_ostree_data.sh" line_range="132" />
<code_context>
+    exit 1
+fi
+
+"get_$category" "$ostree_dir" | "format_${category}_$format"
</code_context>
<issue_to_address>
**suggestion:** Validate category/format values before invoking helper functions to avoid confusing runtime errors

Right now the script assumes any `category`/`format` is valid and directly invokes `get_$category` and `format_${category}_$format`. If a caller passes an unsupported value (e.g., a typo), this will just produce a `command not found` error. Please add explicit validation (e.g., a case statement) for allowed categories and formats, and exit with a clear, actionable error when an invalid value is given.
</issue_to_address>

### Comment 4
<location path="contributing.md" line_range="14-15" />
<code_context>
+* Basic git and github information
+* How to create git commits and submit pull requests
+
+**Bugs and needed implementations** are listed on
+[Github Issues](https://github.com/linux-system-roles/auditd/issues).
+Issues labeled with
+[**help wanted**](https://github.com/linux-system-roles/auditd/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
</code_context>
<issue_to_address>
**nitpick (typo):** Use the correct "GitHub" capitalization in the link text.

Please update the link text to "GitHub Issues" to match the official capitalization and keep the terminology consistent.

Suggested implementation:

```
* Basic git and GitHub information

```

```
[GitHub Issues](https://github.com/linux-system-roles/auditd/issues).

```

```
**Code** is managed on [GitHub](https://github.com/linux-system-roles/auditd), using

```
</issue_to_address>

### Comment 5
<location path="plans/README-plans.md" line_range="3" />
<code_context>
+# Introduction CI Testing Plans
+
+Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) GitHub workflow.
+
+The `plans/test_playbooks_parallel.fmf` plan is a test plan that runs test playbooks in parallel on multiple managed nodes.
</code_context>
<issue_to_address>
**nitpick (typo):** Align "Testing farm" capitalization with the official "Testing Farm" name.

Please update the link text here to "Testing Farm" to match the official name used elsewhere in the docs.

```suggestion
Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing Farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) GitHub workflow.
```
</issue_to_address>

### Comment 6
<location path="plans/README-plans.md" line_range="20" />
<code_context>
+3. For the given role and the given PR, runs the general test from [test.sh](https://github.com/linux-system-roles/tft-tests/blob/main/tests/general/test.sh).
+
+The [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) workflow runs the above plan and uploads the results to our Fedora storage for public access.
+This workflow uses Testing Farm's Github Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm).
+
+## Running Tests
</code_context>
<issue_to_address>
**nitpick (typo):** Use the proper "GitHub Action" capitalization.

Please update "Github Action" to "GitHub Action" in this sentence for correct branding and consistency with the official name.

```suggestion
This workflow uses Testing Farm's GitHub Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm).
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +99 to +103
- name: Commit changes
run: |
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
git add ${{ env.RELEASE_VERSION }}/README.html docs/index.html
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Guard adding docs/index.html so the workflow does not fail when RELEASE_VERSION != 'latest'

docs/index.html is only created when RELEASE_VERSION == 'latest', but git add ... docs/index.html always runs. For tag-based releases, this file won’t exist and the git add will fail. Please either guard git add docs/index.html with RELEASE_VERSION == 'latest' or move it to a separate, conditional git add that matches the copy step’s condition.

Comment thread .github/workflows/tft.yml
Comment on lines +119 to +120
ARTIFACTS_DIR_NAME: "tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\
${{ matrix.platform }}-${{ matrix.ansible_version }}_\
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Avoid referencing a non-existent datetime output from prepare_vars in ARTIFACTS_DIR_NAME

ARTIFACTS_DIR_NAME uses ${{ needs.prepare_vars.outputs.datetime }}, but prepare_vars doesn’t define a datetime output. That will leave an empty segment in the path and diverges from the Set variables with DATETIME step, which recomputes DATETIME and artifact paths. Please either expose DATETIME as an output from prepare_vars and use it here, or remove this interpolation and rely on the value computed in set_vars only.

Suggested implementation:

    env:
      ARTIFACT_TARGET_DIR: /srv/pub/alt/${{ vars.SR_LSR_USER }}/logs

If subsequent steps rely on ARTIFACTS_DIR_NAME as a job-level environment variable, they should be updated to either:

  1. Use the value computed in set_vars (e.g., by echoing it to $GITHUB_OUTPUT in set_vars and then referencing steps.set_vars.outputs.ARTIFACTS_DIR_NAME), or
  2. Recompute the same value locally in their own run sections, mirroring the logic from set_vars.

This will ensure there is a single source of truth for the artifacts directory name and no dependency on a non-existent prepare_vars.outputs.datetime.

exit 1
fi

"get_$category" "$ostree_dir" | "format_${category}_$format"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Validate category/format values before invoking helper functions to avoid confusing runtime errors

Right now the script assumes any category/format is valid and directly invokes get_$category and format_${category}_$format. If a caller passes an unsupported value (e.g., a typo), this will just produce a command not found error. Please add explicit validation (e.g., a case statement) for allowed categories and formats, and exit with a clear, actionable error when an invalid value is given.

Comment thread contributing.md
Comment on lines +14 to +15
**Bugs and needed implementations** are listed on
[Github Issues](https://github.com/linux-system-roles/auditd/issues).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Use the correct "GitHub" capitalization in the link text.

Please update the link text to "GitHub Issues" to match the official capitalization and keep the terminology consistent.

Suggested implementation:

* Basic git and GitHub information

[GitHub Issues](https://github.com/linux-system-roles/auditd/issues).

**Code** is managed on [GitHub](https://github.com/linux-system-roles/auditd), using

Comment thread plans/README-plans.md
@@ -0,0 +1,55 @@
# Introduction CI Testing Plans

Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) GitHub workflow.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Align "Testing farm" capitalization with the official "Testing Farm" name.

Please update the link text here to "Testing Farm" to match the official name used elsewhere in the docs.

Suggested change
Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) GitHub workflow.
Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing Farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) GitHub workflow.

Comment thread plans/README-plans.md
3. For the given role and the given PR, runs the general test from [test.sh](https://github.com/linux-system-roles/tft-tests/blob/main/tests/general/test.sh).

The [tft.yml](https://github.com/linux-system-roles/auditd/blob/main/.github/workflows/tft.yml) workflow runs the above plan and uploads the results to our Fedora storage for public access.
This workflow uses Testing Farm's Github Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Use the proper "GitHub Action" capitalization.

Please update "Github Action" to "GitHub Action" in this sentence for correct branding and consistency with the official name.

Suggested change
This workflow uses Testing Farm's Github Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm).
This workflow uses Testing Farm's GitHub Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm).

@richm richm force-pushed the initial_version branch from 652e6dc to 598bfaf Compare April 15, 2026 17:56
@richm richm requested a review from spetrosi as a code owner April 15, 2026 17:56
@richm richm force-pushed the initial_version branch from 598bfaf to f3a3334 Compare April 15, 2026 18:03
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 15, 2026

[citest]

Copy link
Copy Markdown
Collaborator

@spetrosi spetrosi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good. Did you mention where this role was taken from?

Comment thread meta/argument_specs.yml
Comment thread tasks/main.yml Outdated
# SPDX-License-Identifier: MIT
---
- name: Validate role parameters
ansible.builtin.import_tasks:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why import not include? Please add a comment if there is a reason.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure - this is copied from ansible-role-auditd

Comment thread tasks/main.yml Outdated
- name: Validate role parameters
ansible.builtin.import_tasks:
file: assert.yml
run_once: true
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different hosts might have different variables. Makes sense to me to run this without delegate_to and run_once, so run as a regular task against all hosts.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - not sure - this is copied from ansible-role-auditd

Comment thread tasks/assert.yml
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest calling this file assert_role_vars for clarity.

Comment thread tasks/assert.yml
ansible.builtin.assert:
that:
- auditd_priority_boost | int >= 0
- auditd_priority_boost | int <= 2147483647
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some assert tasks that check int ranges do not have failed_msg, but I guess this is pretty self-explanatory and doesn't require a dedicated message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default message should be informative enough

Comment thread templates/custom.rules.j2 Outdated
Comment on lines +24 to +26
{% endif %}
{% endfor %}
{% endif %}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here again might need some -%} otherwise jinja adds many newlines for each item in the loop. Try running it with more auditd_rules and look at the resulting file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm - the code looks good as-is - here is the output when there are multiple filters in the for loop:

-w /etc/hosts -p rw -k lsr_auditd_integration
-w /etc/group -F auid>=1000 -F guid>=1000 -F name=root -F name=bin -F name=daemon -F name=sys -F name=adm -F name=lp -F name=sync -F name=shutdown -F name=halt -p r -k lsr_auditd_group_filtered
-a always,exit -F arch=b64 -S adjtimex -k lsr_auditd_syscall_adjtimex

Comment thread vars/main.yml Outdated
Comment thread vars/main.yml Outdated
Comment thread meta/collection-requirements.yml
Comment thread README.md
by `meta/argument_specs.yml` and `tasks/assert.yml` using the same limits as the
audit-userspace parsers (for example `num_logs` ≤ 999).

### auditd_local_events
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be clearer and more redable to place Type and Default value for each variable on a separate line. Now each var's description is one long line that's hard to process.

A not for future, maybe when we introduce argument_spec in all roles, we can make it the source of truth, and generate variables section in README from info in argument_spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be clearer and more redable to place Type and Default value for each variable on a separate line. Now each var's description is one long line that's hard to process.

ok

A not for future, maybe when we introduce argument_spec in all roles, we can make it the source of truth, and generate variables section in README from info in argument_spec.

Yes, good idea

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 16, 2026

Looks very good. Did you mention where this role was taken from?

In the README.md - "This role is heavily based on ansible-role-auditd"

@richm richm force-pushed the initial_version branch 3 times, most recently from 930725f to 8862d20 Compare April 16, 2026 15:14
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 16, 2026

[citest]

Comment thread tasks/assert.yml
that:
- auditd_freq | int >= 0
- auditd_freq | int <= 2147483647
fail_msg: auditd_freq must be between 0 and INT_MAX
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.ansible.com/projects/ansible/latest/dev_guide/developing_program_flow_modules.html#argument-spec
this link documents the use of the following keywords, we maybe we can make use of them to replace these checks.

mutually_exclusive:
required_together:
required_one_of:
required_if:
required_by:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this - I tried adding:

    required_if:
      - [auditd_max_log_file_action, exec, [auditd_max_log_file_action_exe], true]

My editor flags this as Property required_if is not allowed.yaml-schema: Entry Point(513)

At runtime - it appears this is ignored - ansible doesn't issue a warning or an error - but it doesn't work - in tests_invalid_input.yml the implicit argument validation is not hit, it falls through to the explicit check Assert exec companion paths when action is exec

Looking at the role argument spec page - https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_reuse_roles.html#specification-format - it doesn't list those options, nor does it say that you can also use the module argument specs

So I would say we can't do this and we are limited to whatever is specified at https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_reuse_roles.html#specification-format

@Cropi
Copy link
Copy Markdown
Collaborator

Cropi commented Apr 17, 2026

Right now the role defaults come from clear_config() in auditd-config.c, but users get init.d/auditd.conf on Fedora/RHEL/CentOS. I think the role should match that instead. Some important values differ. I think auditd has them for historical reasons.

Some of the defaults that don't match:

Parameter Role default Shipped default
flush none INCREMENTAL_ASYNC
freq 0 50
max_log_file 0 8
num_logs 0 5
max_log_file_action ignore ROTATE
space_left 0 75
space_left_action ignore SYSLOG
admin_space_left 0 50
admin_space_left_action ignore SUSPEND
disk_full_action ignore SUSPEND
disk_error_action syslog SUSPEND

tcp_listen_port: 0 -- the parser rejects values < 1 when compiled with USE_LISTENER (all Fedora/RHEL/CentOS builds). The shipped config comments this out (##tcp_listen_port = 60); if the line isn't present, the parser never runs and the internal 0 (disabled) is kept. The role should omit it when 0. Same for tcp_client_ports, krb5_key_file, and enable_krb5.

space_left: 0 + admin_space_left: 0 -- after parsing, sanity_check() in auditd-config.c requires space_left > admin_space_left. When both are 0, that fails: Error - space_left(0) must be larger than admin_space_left(0). The shipped config uses 75 and 50.

The shipped config uses uppercase keywords (ROTATE, SYSLOG, SUSPEND). Not functional, but the role should match for consistency.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 17, 2026

Right now the role defaults come from clear_config() in auditd-config.c, but users get init.d/auditd.conf on Fedora/RHEL/CentOS.

Are these values different on different versions of EL and Fedora? We are targeting EL8 and later. Are the values on EL8 different than EL9 and EL10 and Fedora? Are they different on different minor versions e.g. between EL10.0 and EL10.2?

I think the role should match that instead.

I agree, but it may be difficult depending on how different the values are between major and minor versions of the OS.

Some important values differ. I think auditd has them for historical reasons.

Some of the defaults that don't match:
Parameter Role default Shipped default
flush none INCREMENTAL_ASYNC
freq 0 50
max_log_file 0 8
num_logs 0 5
max_log_file_action ignore ROTATE
space_left 0 75
space_left_action ignore SYSLOG
admin_space_left 0 50
admin_space_left_action ignore SUSPEND
disk_full_action ignore SUSPEND
disk_error_action syslog SUSPEND

tcp_listen_port: 0 -- the parser rejects values < 1 when compiled with USE_LISTENER (all Fedora/RHEL/CentOS builds). The shipped config comments this out (##tcp_listen_port = 60); if the line isn't present, the parser never runs and the internal 0 (disabled) is kept. The role should omit it when 0. Same for tcp_client_ports, krb5_key_file, and enable_krb5.

space_left: 0 + admin_space_left: 0 -- after parsing, sanity_check() in auditd-config.c requires space_left > admin_space_left. When both are 0, that fails: Error - space_left(0) must be larger than admin_space_left(0). The shipped config uses 75 and 50.

The shipped config uses uppercase keywords (ROTATE, SYSLOG, SUSPEND). Not functional, but the role should match for consistency.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 17, 2026

Same for tcp_client_ports, krb5_key_file, and enable_krb5

Looking at the code - https://github.com/linux-audit/audit-userspace/blob/master/src/auditd-config.c#L1677-L1699 - setting enable_krb: false does nothing - so I think the code as it stands is ok

enable_krb5 = {{ auditd_yesno(auditd_enable_krb5) }}

if auditd_enable_krb5 is true, then write enable_krb5 = yes which overrides transport to be KRB5
if auditd_enable_krb5 is false, then write enable_krb5 = no which is a no-op - use the default value of transport which is TCP which is the desired behavior

@richm richm force-pushed the initial_version branch from 8862d20 to 0623a8c Compare April 17, 2026 21:14
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 17, 2026

[citest]

@Cropi
Copy link
Copy Markdown
Collaborator

Cropi commented Apr 20, 2026

Next round of findings, this time focused on the rules-level settings:

  1. --backlog_wait_time is missing. The shipped 10-base-config.rules sets --backlog_wait_time 60000 and it's commonly tuned in production. Should be exposed as a variable.

  2. auditd_maximum_rate should be omittable. The shipped rules don't set -r, so the kernel default is 0 (no limit). A default of 60 msg/sec will throttle audit messages and can drop events on busy systems. I'd not emit -r by default but keep it as an optional parameter for users who want rate limiting.

  3. --loginuid-immutable is missing. Shipped 11-loginuid.rules enables this and it's standard hardening. A boolean that emits --loginuid-immutable when true, skipped otherwise.

  4. keyname -- recommended but shouldn't be required. The -k flag is optional in auditctl. I agree it should be used since it makes searching and reporting way easier, but the role shouldn't enforce it. Treat it as optional in code, document it as strongly recommended.

  5. auditd_default_arch -- consider not emitting arch by default. When no -F arch= is specified, auditctl resolves syscall names using the native arch but sends the rule to the kernel without an arch filter, so the kernel matches against any architecture. It does warn if syscall numbers differ between b32/b64 since the number in the rule is only resolved for the native arch.

    The common production pattern (see 30-stig.rules, 41-containers.rules) is to emit rules in pairs -- b32 and b64 -- for correct syscall number resolution on each. With auditd_default_arch: b64, users only get rules for one arch and can easily miss b32 coverage on 64-bit systems. Omitting arch by default is slower. Users who need per-arch control or the performance gain can specify arch on individual rules.

Comment thread tests/tests_invalid_input.yml Outdated
# Each block runs include_role expecting failure; rescue records that outcome.
- name: Verify invalid parameters are rejected
hosts: all
gather_facts: true
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
gather_facts: true
gather_facts: false

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 20, 2026

Next round of findings, this time focused on the rules-level settings:

1. **`--backlog_wait_time` is missing.** The shipped `10-base-config.rules` sets `--backlog_wait_time 60000` and it's commonly tuned in production. Should be exposed as a variable.

What should be the default value for auditd_backlog_wait_time? What value should mean omit?

2. **`auditd_maximum_rate` should be omittable.** The shipped rules don't set `-r`, so the kernel default is 0 (no limit). A default of 60 msg/sec will throttle audit messages and can drop events on busy systems. I'd not emit `-r` by default but keep it as an optional parameter for users who want rate limiting.

Make the default value auditd_maximum_rate: 0 and 0 means omit?

3. **`--loginuid-immutable` is missing.** Shipped `11-loginuid.rules` enables this and it's standard hardening. A boolean that emits `--loginuid-immutable` when true, skipped otherwise.

4. **`keyname` -- recommended but shouldn't be required.** The `-k` flag is optional in auditctl. I agree it should be used since it makes searching and reporting way easier, but the role shouldn't enforce it. Treat it as optional in code, document it as strongly recommended.

Ok. I also see from the auditctl man page that a rule can have multiple keynames? Also, when to use -k and when to use -F key=? e.g. use -k on EL9 and earlier, and use -F key= on EL10 and later and Fedora?

5. **`auditd_default_arch` -- consider not emitting arch by default.** When no `-F arch=` is specified, auditctl resolves syscall names using the native arch but sends the rule to the kernel without an arch filter, so the kernel matches against any architecture. It does warn if syscall numbers differ between b32/b64 since the number in the rule is only resolved for the native arch.

The man pages for audit.rules and auditctl strongly suggest adding -b arch or -F arch= for syscall rules - perhaps we could do it like this?

  • auditd_default_arch: b64 is the default
  • If a user specifies a syscall rule without arch, it gets set to -F arch={{ auditd_default_arch }}
  • A user can explicitly set arch: b32 or arch: b64 in a rule to override the default
  • If a user really wants a syscall rule with no arch, they can set arch: omit or (arch: "" or arch: null or arch: none or arch: all or some other token value which means do not set the arch - and will not warn either)

Alternately - we get rid of auditd_default_arch and always force users to specify the arch - if no arch is specified, we warn the user - user can set special value arch: nowarn which means "yes, I know this is not recommended, but I'm going to do it anyway, and don't warn me"

Should we allow the user to specify multiple arch in a rule? e.g.

  - action: always
    filter: exit
    arch: [b32, b64]
    ....

which will generate both the b32 and b64 version of the rule?

Also - when to use -b versus -F arch? Use -b on EL9 and earlier, use -F arch= on EL10 and later?

   The common production pattern (see `30-stig.rules`, `41-containers.rules`) is to emit rules in pairs -- `b32` and `b64` -- for correct syscall number resolution on each. With `auditd_default_arch: b64`, users only get rules for one arch and can easily miss b32 coverage on 64-bit systems. Omitting arch by default is slower. Users who need per-arch control or the performance gain can specify `arch` on individual rules.

OK - see above?

Additional questions:

-a action,list (or list,action)

  • should we check if action and list are valid? e.g. check that action in [always, never] and list in [task, exit, ....]?
  • which combinations of -a action,list are valid without a watch or syscall or filter?
  • Should we call this action and list instead of action and filter?
  • Is using -a action,list -k keyname an error?

-w, -F path=, -F dir=, -q, -p, -F perm=, -t

  • when to use -w vs -F path=/-F dir=? The problem is that if the user specifies file: /some/path we don't know if that will be a file or a dir if it doesn't yet exist, so we can't automatically convert -w to -F path=/-F dir=
  • we could just add path: and dir: keys - user will need to use them in order not to use the deprecated -w
  • when/how is the -q option used? can it be used with both -w, and with -F path=/-F dir=? Is there a -F something= equivalent of -q?
  • when to use -p vs -F perm=? EL9 vs. EL10?
  • when using -w it seems common to begin the rule with -w /some/path -F filters...., but with -F path=/-F dir= it seems the rule must begin with -a always,exit -F arch=... -F path=.... -F filters....
  • when using -w can you also specify -b or -F arch=? Or only with -F path=/-F dir=?
  • when using -w can you also specify -S syscall? Or only with -F path=/-F dir=?
  • do we need to handle the -t trim option? or is that only used with interactive auditctl?
  • is it possible to specify multiple -w or -F path= or -F dir= in a single rule? If so, can you specify different -p or -F perm= for each file? if so, how does that work?

-S

Looks like this should be a list instead of a single value:

  - action: always
    filter: exit
    syscall: [accept,connect]

and that can be written either as -S accept,connect or -S accept -S connect? Which style should we prefer?

-F exe=

Should we make this a separate key exe: /usr/bin/ls, or just have users specify it in the filter list filters: [exe=/usr/bin/ls,...other filters...] ?

@Cropi
Copy link
Copy Markdown
Collaborator

Cropi commented Apr 21, 2026

Q1: --backlog_wait_time default and omit value

The default should be 60000 to match the shipped 10-base-config.rules. Even though this is the same as the kernel default, making it explicit in the generated rules is better — it's what the upstream ships and it makes the value visible to anyone reading the rules file. To omit the line entirely, the user would set the variable to null ??. We shouldn't use 0 as the omit sentinel because 0 is a valid value that disables backlog waiting entirely.

Q2: auditd_maximum_rate default and 0 means omit

Do we really need to provide defaults for all of these rule-level settings? The shipped rules don't set -r at all, and the kernel default of 0 (no limit) is the right behavior for most systems. The template should check whether the variable is defined and only render -r when it is. Is that possible?

Q3: keyname — -k vs -F key=, multiple keys

-k and -F key= are functionally identical — both call the same process_key_option() internally. All shipped rules files use -F key= exclusively, and the man page marks -k as deprecated when used with watches. The role should just use -F key= everywhere — no need to vary by OS version. Multiple keys per rule are supported; they get concatenated up to the AUDIT_MAX_KEY_LEN limit of 256 bytes (defined in linux/audit.h). The keyname field should remain optional and accept either a single string or a list of strings.

Q4: auditd_default_arch — how to handle arch

I'm not aware of any other option for specifying arch besides -F arch=, so the role should just use that. The idea of specifying multiple arches in a single rule definition (e.g. arch: [b32, b64]) and having the role generate separate audit rules for each is nice. I'd go with the alternative approach — always require users to specify the arch for syscall rules since that mirrors how audit itself works. If no arch is specified, warn the user. Users who intentionally want no arch can set a special value to suppress the warning.

Q5: -a action,list questions

Yes, we should validate action and list values — the valid actions are always and never, and the valid lists are task, exit, user, exclude, filesystem, and io_uring. These are stable, well-defined sets. Using -a action,list -k keyname without a syscall, watch, or other filter is an error — auditctl rejects it. As for naming, the man page uses action and list in the syntax (-a list,action) but in the descriptions list is often referred to as filter. Either naming works, but we should pick one and be consistent. Additionally, it's hard to answer which combinations of -a action,list are valid without a watch or syscall or filter — there are a lot of combinations. I could gather the different options for each list type but that might be overkill. It's also not a hardcoded list — ultimately the kernel is the one who decides if a rule is valid or not. There are some checks happening in lib/libaudit.c but the final validation is done by the kernel.

Q6: -w, -F path=, -F dir=, -q, -p, -F perm=, -t

The role should use the syscall form exclusively — -w and -p are both deprecated. Instead of the current file: key, the role should provide separate path: and dir: keys so users explicitly specify what they're watching, matching -F path= and -F dir=. This also solves the problem of not knowing whether a target is a file or directory. -F perm= replaces -p. As for -q and -t, these are standalone commands for managing directory watch subtrees at runtime, not rule definitions — they're probably out of scope for the role for now. Only one path or dir can be specified in a single rule.

Q7: -S syscall — list vs single value

Yes, syscall should accept a list and render as comma-separated (-S adjtimex,settimeofday), matching the shipped rules style. The man page recommends this for performance.

Q8: -F exe= — separate key or filter

Keep it in the filter list. In the shipped rules, exe= is just another -F field used alongside other filters. There's no reason to treat it differently — a separate key would add complexity without benefit.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 21, 2026

Q1: --backlog_wait_time default and omit value

The default should be 60000 to match the shipped 10-base-config.rules. Even though this is the same as the kernel default, making it explicit in the generated rules is better — it's what the upstream ships and it makes the value visible to anyone reading the rules file. To omit the line entirely, the user would set the variable to null ??. We shouldn't use 0 as the omit sentinel because 0 is a valid value that disables backlog waiting entirely.

ok - I guess we'll have to use null as the value which means "omit this parameter". The issue is that the data type for the variable is currently int, so it would be better to pick an int value. Using null means that we can't use int here - https://github.com/linux-system-roles/auditd/pull/1/changes#diff-54476ea7257304a760f273caebc0ca7ae6c3f599571bf52891d260fa99ccfe95 - which means special handling to check for values.

Q2: auditd_maximum_rate default and 0 means omit

Do we really need to provide defaults for all of these rule-level settings?

Every parameter that can be set by the user must be declared in defaults/main.yml and must have some sort of default value. However, the default can be null if there is no other suitable value. For example, if we want to allow the user to explicitly set the value of 0, then we'll have to use null to mean "omit this value".

The shipped rules don't set -r at all, and the kernel default of 0 (no limit) is the right behavior for most systems. The template should check whether the variable is defined

We can't use auditd_maximum_rate is defined as the test since it will always be defined. We'll have to use auditd_maximum_rate is not null.

and only render -r when it is. Is that possible?

Yes, it will just take some additional validation logic.

Q3: keyname — -k vs -F key=, multiple keys

-k and -F key= are functionally identical — both call the same process_key_option() internally. All shipped rules files use -F key= exclusively, and the man page marks -k as deprecated when used with watches. The role should just use -F key= everywhere — no need to vary by OS version. Multiple keys per rule are supported; they get concatenated up to the AUDIT_MAX_KEY_LEN limit of 256 bytes (defined in linux/audit.h). The keyname field should remain optional and accept either a single string or a list of strings.

ok - I don't think I will add validation for total keyname lengths < 256 unless that is a bad failure case.

Q4: auditd_default_arch — how to handle arch

I'm not aware of any other option for specifying arch besides -F arch=, so the role should just use that. The idea of specifying multiple arches in a single rule definition (e.g. arch: [b32, b64]) and having the role generate separate audit rules for each is nice. I'd go with the alternative approach — always require users to specify the arch for syscall rules since that mirrors how audit itself works. If no arch is specified, warn the user. Users who intentionally want no arch can set a special value to suppress the warning.

ok.

Q5: -a action,list questions

Yes, we should validate action and list values — the valid actions are always and never, and the valid lists are task, exit, user, exclude, filesystem, and io_uring. These are stable, well-defined sets. Using -a action,list -k keyname without a syscall, watch, or other filter is an error — auditctl rejects it. As for naming, the man page uses action and list in the syntax (-a list,action) but in the descriptions list is often referred to as filter. Either naming works, but we should pick one and be consistent. Additionally, it's hard to answer which combinations of -a action,list are valid without a watch or syscall or filter — there are a lot of combinations. I could gather the different options for each list type but that might be overkill. It's also not a hardcoded list — ultimately the kernel is the one who decides if a rule is valid or not. There are some checks happening in lib/libaudit.c but the final validation is done by the kernel.

ok. I'll add a check for valid action and list. I will leave the naming for filter as is, and not rename it to list. I don't think we will add validation for valid combinations of action/filter.

Q6: -w, -F path=, -F dir=, -q, -p, -F perm=, -t

The role should use the syscall form exclusively — -w and -p are both deprecated. Instead of the current file: key, the role should provide separate path: and dir: keys so users explicitly specify what they're watching, matching -F path= and -F dir=. This also solves the problem of not knowing whether a target is a file or directory. -F perm= replaces -p. As for -q and -t, these are standalone commands for managing directory watch subtrees at runtime, not rule definitions — they're probably out of scope for the role for now. Only one path or dir can be specified in a single rule.

ok

I assume that having a single rule with both path and dir is invalid, and having a rule with multiple path or multiple dir is also invalid.

Q7: -S syscall — list vs single value

Yes, syscall should accept a list and render as comma-separated (-S adjtimex,settimeofday), matching the shipped rules style. The man page recommends this for performance.

ok

Q8: -F exe= — separate key or filter

Keep it in the filter list. In the shipped rules, exe= is just another -F field used alongside other filters. There's no reason to treat it differently — a separate key would add complexity without benefit.

ok

@spetrosi
Copy link
Copy Markdown
Collaborator

spetrosi commented Apr 21, 2026

Every parameter that can be set by the user must be declared in defaults/main.yml and must have some sort of default value. However, the default can be null if there is no other suitable value. For example, if we want to allow the user to explicitly set the value of 0, then we'll have to use null to mean "omit this value".

We discussed setting to null vs setting to an empty line. And you and Peter were for the empty line, and I eventually like empty line more than null. For lists and dicts empty would be [] and {}.

redhat-cop/infra.leapp#349 (comment)

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 21, 2026

Every parameter that can be set by the user must be declared in defaults/main.yml and must have some sort of default value. However, the default can be null if there is no other suitable value. For example, if we want to allow the user to explicitly set the value of 0, then we'll have to use null to mean "omit this value".

We discussed setting to null vs setting to an empty line. And you and Peter were for the empty line, and I eventually like empty line more than null. For lists and dicts empty would be [] and {}.

redhat-cop/infra.leapp#349 (comment)

Ok

@spetrosi
Copy link
Copy Markdown
Collaborator

Thinking more about this, empty strings can be ambiguous in the cases when empty value is a proper value too. In leapp it was about Satellite Orgs that cannot be empty, so empty definitely means omit. But there may be cases when empty string value might be a value different from omit.
null is not ambiguous and definitely means omit... wdyt?

@spetrosi
Copy link
Copy Markdown
Collaborator

Consider an option where empty string is a real value. I use the role to set the value to foo. Then, I want to reset it back to an empty string - I won't be able to do this with the role because the task will be skipped because of |length>0. And if we omit this check, then the value will be rewritten back to an empty string if I omit it (I'd expect that role won't touch the value).
I do not know how argument_spec would like it because null is not a string...

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 21, 2026

Consider an option where empty string is a real value. I use the role to set the value to foo. Then, I want to reset it back to an empty string - I won't be able to do this with the role because the task will be skipped because of |length>0. And if we omit this check, then the value will be rewritten back to an empty string if I omit it (I'd expect that role won't touch the value).

I think null makes sense for auditd_maximum_rate and auditd_backlog_wait_time to mean omit this setting since they are of type int and there isn't another appropriate int value for this.

I do not know how argument_spec would like it because null is not a string...

We use type: raw in argument_spec, and have some additional logic in assert_role_vars.yml

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 21, 2026

@Cropi

  • It looks like every rule must start with -a action,filter - is this true? And both action and filter must always be specified?
  • It looks like every path/dir rule must start with -a always,exit or -a never,exit - is this true?
  • It looks like every syscall rule must start with -a always,exit or -a never,exit - is this true?
  • It looks like the perm= can be omitted safely (that is, we don't need to warn the user about the performance penalty) in a path/dir rule if the actual syscalls used are provided - from the included rules
-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

I guess this is the same as using perm=w except that specifying the open system call excludes other system calls/arguments which might have been included by the perm=w? Although it seems pretty tricky to know that if the user specifies path/dir with syscall, we don't have to warn about missing perm.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

* It looks like every syscall rule must start with `-a always,exit` or `-a never,exit` - is this true?

I guess not - looks like some of them can begin with -a always,io_uring or -a never,io_uring: https://www.man7.org/linux/man-pages/man7/audit.rules.7.html

       -a always,io_uring -S openat,openat2 -F key=access

@richm richm force-pushed the initial_version branch from 0623a8c to 3b4faf1 Compare April 22, 2026 01:04
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

[citest]

@richm richm force-pushed the initial_version branch from 3b4faf1 to 8f0df05 Compare April 22, 2026 01:17
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

@spetrosi @Cropi I guess start with the README.md to see the rule changes.

I'm still not happy with the rule keywords - I'm using filter for the action,filter, and using field for the list of fields/filters to apply to the rule. I think the old way of using both filter and filters for two different things was too confusing. The man pages use field for the list of -F field_nameOPvalue.

I got the rule key/value combinations using the feedback from @Cropi and reading the audit.rules and auditctl man pages.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

[citest]

@Cropi
Copy link
Copy Markdown
Collaborator

Cropi commented Apr 22, 2026

@Cropi

* It looks like every rule must start with `-a action,filter` - is this true?  And both `action` and `filter` must always be specified?

Generally, yes except for the legacy -w rules. But that shouldn't really affect us since we're pushing for the newer syntax anyway.
On another observation, since the kernel allows both appending (-a) and prepending (-A), could we set -a as the default but let users switch to -A if they need to? It might be worth having a default 'insertion strategy'.

* It looks like every path/dir rule must start with `-a always,exit` or `-a never,exit` - is this true?

Yes.

* It looks like every syscall rule must start with `-a always,exit` or `-a never,exit` - is this true?

Almost. Syscall rules can also use io_uring as a filter (e.g. -a always,io_uring ...). io_uring is relatively new enhancement in the kernel, it's not present in rhel8.

* It looks like the `perm=` can be omitted safely (that is, we don't need to warn the user about the performance penalty)  in a path/dir rule if the actual syscalls used are provided - from the included rules
-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

I guess this is the same as using perm=w except that specifying the open system call excludes other system calls/arguments which might have been included by the perm=w? Although it seems pretty tricky to know that if the user specifies path/dir with syscall, we don't have to warn about missing perm.

If a user specifies a path/dir rule with explicit syscalls (-S), omitting perm is fine and doesn't need a warning. Btw, adding perm=w internally maps to ~20 syscalls different syscalls.
A good example could be applying the following rule

-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=w

and checking that it would be internally turned into

-a always,exit -F arch=b64 -S open,bind,truncate,ftruncate,rename,mkdir,rmdir,creat,link,unlink,symlink,mknod,acct,swapon,quotactl,openat,mkdirat,mknodat,unlinkat,renameat,linkat,symlinkat,fallocate,renameat2,openat2 -F path=/etc/passwd -F perm=w

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

@Cropi

* It looks like every rule must start with `-a action,filter` - is this true?  And both `action` and `filter` must always be specified?

Generally, yes except for the legacy -w rules. But that shouldn't really affect us since we're pushing for the newer syntax anyway. On another observation, since the kernel allows both appending (-a) and prepending (-A), could we set -a as the default but let users switch to -A if they need to? It might be worth having a default 'insertion strategy'.

Not sure what you mean - we generate the entire custom.rules file every time, in the order given by the user. Is there a case where we would generate a file like this?

-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change
-A always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
-A always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change
* It looks like every path/dir rule must start with `-a always,exit` or `-a never,exit` - is this true?

Yes.

* It looks like every syscall rule must start with `-a always,exit` or `-a never,exit` - is this true?

Almost. Syscall rules can also use io_uring as a filter (e.g. -a always,io_uring ...). io_uring is relatively new enhancement in the kernel, it's not present in rhel8.

Ok. I might need to add a conditional for that for EL8.

I guess this is the same as using perm=w except that specifying the open system call excludes other system calls/arguments which might have been included by the perm=w? Although it seems pretty tricky to know that if the user specifies path/dir with syscall, we don't have to warn about missing perm.

If a user specifies a path/dir rule with explicit syscalls (-S), omitting perm is fine and doesn't need a warning. Btw, adding perm=w internally maps to ~20 syscalls different syscalls. A good example could be applying the following rule

-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=w

and checking that it would be internally turned into

-a always,exit -F arch=b64 -S open,bind,truncate,ftruncate,rename,mkdir,rmdir,creat,link,unlink,symlink,mknod,acct,swapon,quotactl,openat,mkdirat,mknodat,unlinkat,renameat,linkat,symlinkat,fallocate,renameat2,openat2 -F path=/etc/passwd -F perm=w

ok - so allow specifying path/dir with either perm or syscall with no warning

@richm richm force-pushed the initial_version branch from 8f0df05 to 3d601d9 Compare April 22, 2026 12:39
@Cropi
Copy link
Copy Markdown
Collaborator

Cropi commented Apr 22, 2026

@Cropi

* It looks like every rule must start with `-a action,filter` - is this true?  And both `action` and `filter` must always be specified?

Generally, yes except for the legacy -w rules. But that shouldn't really affect us since we're pushing for the newer syntax anyway. On another observation, since the kernel allows both appending (-a) and prepending (-A), could we set -a as the default but let users switch to -A if they need to? It might be worth having a default 'insertion strategy'.

Not sure what you mean - we generate the entire custom.rules file every time, in the order given by the user. Is there a case where we would generate a file like this?

I keep forgetting this is not a wrapper for auditctl CLI but for the whole audit config + rules. In that case it's enough if all rules will be appended via (-a). Sorry for the confusion.

-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change
-A always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
-A always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change
* It looks like every path/dir rule must start with `-a always,exit` or `-a never,exit` - is this true?

Yes.

* It looks like every syscall rule must start with `-a always,exit` or `-a never,exit` - is this true?

Almost. Syscall rules can also use io_uring as a filter (e.g. -a always,io_uring ...). io_uring is relatively new enhancement in the kernel, it's not present in rhel8.

Ok. I might need to add a conditional for that for EL8.

I guess this is the same as using perm=w except that specifying the open system call excludes other system calls/arguments which might have been included by the perm=w? Although it seems pretty tricky to know that if the user specifies path/dir with syscall, we don't have to warn about missing perm.

If a user specifies a path/dir rule with explicit syscalls (-S), omitting perm is fine and doesn't need a warning. Btw, adding perm=w internally maps to ~20 syscalls different syscalls. A good example could be applying the following rule

-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=w

and checking that it would be internally turned into

-a always,exit -F arch=b64 -S open,bind,truncate,ftruncate,rename,mkdir,rmdir,creat,link,unlink,symlink,mknod,acct,swapon,quotactl,openat,mkdirat,mknodat,unlinkat,renameat,linkat,symlinkat,fallocate,renameat2,openat2 -F path=/etc/passwd -F perm=w

ok - so allow specifying path/dir with either perm or syscall with no warning

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

[citest]

@richm richm force-pushed the initial_version branch from 3d601d9 to f47a0a8 Compare April 22, 2026 12:56
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

[citest]

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 22, 2026

@Cropi @spetrosi please review

Copy link
Copy Markdown
Collaborator

@Cropi Cropi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In assert_role_vars.yml, the check for tcp_max_per_addr allows zero, but auditd itself rejects anything below 1 — so the role would happily write a config that auditd refuses to load. The lower bound should be 1.

Also, auditd_enable_flag, auditd_fail_mode, and auditd_buffer_size go straight into the rules file without any range validation. The first two only make sense as 0, 1, or 2, and buffer size needs to be positive.

These are small changes though, can be addressed in a follow-up.

@richm richm force-pushed the initial_version branch from f47a0a8 to 751f3bd Compare April 29, 2026 13:53
@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 29, 2026

In assert_role_vars.yml, the check for tcp_max_per_addr allows zero, but auditd itself rejects anything below 1 — so the role would happily write a config that auditd refuses to load. The lower bound should be 1.

Also, auditd_enable_flag, auditd_fail_mode, and auditd_buffer_size go straight into the rules file without any range validation. The first two only make sense as 0, 1, or 2, and buffer size needs to be positive.

These are small changes though, can be addressed in a follow-up.

Done.

@richm
Copy link
Copy Markdown
Contributor Author

richm commented Apr 29, 2026

[citest]

@richm richm force-pushed the initial_version branch 2 times, most recently from 2386b0e to 1186a27 Compare May 4, 2026 22:14
@richm
Copy link
Copy Markdown
Contributor Author

richm commented May 4, 2026

[citest]

@richm
Copy link
Copy Markdown
Contributor Author

richm commented May 5, 2026

[citest_bad]

Comment thread tasks/assert_role_vars.yml Outdated
- auditd_freq | int <= 2147483647
fail_msg: auditd_freq must be between 0 and INT_MAX

- name: Assert incremental flush requires non-zero freq (sanity_check)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can indeed replace sanity here

This is the initial commit of the new role auditd.

Signed-off-by: Rich Megginson <rmeggins@redhat.com>
@richm richm force-pushed the initial_version branch from 1186a27 to 2ce476a Compare May 5, 2026 13:12
@richm
Copy link
Copy Markdown
Contributor Author

richm commented May 5, 2026

[citest]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants