From 66b01d9b55ee6b1d791383467827a6444673a51c Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Wed, 29 Apr 2020 18:36:39 +0200 Subject: [PATCH 1/6] Add fields arg to ansbile audit syscall macros The field arg allows one to specify syscall fields for the audit rule. These fields can be auid, exit, argument, or any field used by audit. Reference: https://github.com/linux-audit/audit-documentation/wiki/SPEC-Writing-Good-Events#field-names --- shared/macros-ansible.jinja | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja index 03e4306051..7674c290fa 100644 --- a/shared/macros-ansible.jinja +++ b/shared/macros-ansible.jinja @@ -352,9 +352,11 @@ The macro requires following parameters: - arch: an architecture to be used in the Audit rule (b32, b64) - syscalls: list of syscalls supplied as a list ["syscall1", "syscall2"] etc. - key: a key to use as rule identifier. +- fields (optional): list of syscall fields to add (e.g.: auid=unset, exit=-EPERM, a0&0100); + Add them in the order you expect them to be in the audit rule. Note that if there already exists a rule wit the same key in the /etc/audit/rules.d directory, the rule will be placed in the same file. #}} -{{% macro ansible_audit_augenrules_add_syscall_rule(arch="", syscalls=[], key="") -%}} +{{% macro ansible_audit_augenrules_add_syscall_rule(arch="", syscalls=[], key="", fields=[]) -%}} - name: Declare list of syscals set_fact: syscalls: {{{ syscalls }}} @@ -362,10 +364,17 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul - name: Declare number of syscalls set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}" +{{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} +{{% set fields_data = { 'regex' : "", 'list': "" } %}} +{{% for field in fields %}} + {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F\s+' + field + ')'}) %}} + {{% set not_used = fields_data.update({'list': fields_data.list+ ' -F ' + field }) %}} +{{% endfor %}} + - name: Check existence of syscalls for architecture {{{ arch }}} in /etc/audit/rules.d/ find: paths: "/etc/audit/rules.d" - contains: '^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch={{{ arch }}}[\s]+)(?:.*(-S[\s]+{{ item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$' + contains: '^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch={{{ arch }}}[\s]+)(?:.*(-S[\s]+{{ item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*{{{ fields_data.regex }}}(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$' patterns: "*.rules" register: audit_syscalls_found_{{{ arch }}}_rules_d loop: "{{ syscalls }}" @@ -401,7 +410,7 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul loop: "{{ audit_syscalls_found_{{{ arch }}}_rules_d.results }}" when: item.matched is defined and item.matched == 0 - name: "Construct rule: add key" - set_fact: tmpline="{{ tmpline + '-k {{{ key }}}' }}" + set_fact: tmpline="{{ tmpline + '{{{ fields_data.list }}} -k {{{ key }}}' }}" - name: "Insert the line in {{ all_files[0] }}" lineinfile: path: "{{ all_files[0] }}" @@ -417,8 +426,10 @@ The macro requires following parameters: - arch: an architecture to be used in the Audit rule (b32, b64) - syscalls: list of syscalls supplied as a list ["syscall1", "syscall2"] etc. - key: a key to use as rule identifier. +- fields (optional): list of syscall fields to add (e.g.: auid=unset, exit=-EPERM, a0&0100); + Add them in the order you expect them to be in the audit rule. #}} -{{% macro ansible_audit_auditctl_add_syscall_rule(arch="", syscalls=[], key="") -%}} +{{% macro ansible_audit_auditctl_add_syscall_rule(arch="", syscalls=[], key="", fields=[]) -%}} - name: Declare list of syscals set_fact: syscalls: {{{ syscalls }}} @@ -426,10 +437,17 @@ The macro requires following parameters: - name: Declare number of syscalls set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}" +{{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} +{{% set fields_data = { 'regex' : "", 'list': "" } %}} +{{% for field in fields %}} + {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F\s+' + field + ')'}) %}} + {{% set not_used = fields_data.update({'list': fields_data.list + ' -F ' + field }) %}} +{{% endfor %}} + - name: Check existence of syscalls for architecture {{{ arch }}} in /etc/audit/audit.rules find: paths: "/etc/audit" - contains: '^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch={{{ arch }}}[\s]+)(?:.*(-S[\s]+{{ item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$' + contains: '^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch={{{ arch }}}[\s]+)(?:.*(-S[\s]+{{ item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*{{{ fields_data.regex }}}(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$' patterns: "audit.rules" register: audit_syscalls_found_{{{ arch }}}_audit_rules loop: "{{ syscalls }}" @@ -445,8 +463,8 @@ The macro requires following parameters: set_fact: tmpline="{{tmpline + '-S ' + item.item + ' ' }}" loop: "{{ audit_syscalls_found_{{{ arch }}}_audit_rules.results }}" when: item.matched is defined and item.matched == 0 - - name: "Construct rule: add key" - set_fact: tmpline="{{ tmpline + '-k {{{ key }}}' }}" + - name: "Construct rule: add fields and key" + set_fact: tmpline="{{ tmpline + '{{{ fields_data.list }}} -k {{{ key }}}' }}" - name: Insert the line in /etc/audit/audit.rules lineinfile: path: "/etc/audit/audit.rules" From 5de069a558c4456d0610764d8fc9da23f0ba294e Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Wed, 29 Apr 2020 18:43:08 +0200 Subject: [PATCH 2/6] Fix spacing between syscalls and fields By having the white space at the beginning of the token, it is easy to concatenate them without worries. --- shared/macros-ansible.jinja | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja index 7674c290fa..2aaf0c366b 100644 --- a/shared/macros-ansible.jinja +++ b/shared/macros-ansible.jinja @@ -404,12 +404,12 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul - name: "Insert the syscall rule in {{ all_files[0] }}" block: - name: "Construct rule: add rule list, action and arch" - set_fact: tmpline="-a always,exit -F arch={{{ arch }}} " + set_fact: tmpline="-a always,exit -F arch={{{ arch }}}" - name: "Construct rule: add syscalls" - set_fact: tmpline="{{tmpline + '-S ' + item.item + ' ' }}" + set_fact: tmpline="{{tmpline + ' -S ' + item.item }}" loop: "{{ audit_syscalls_found_{{{ arch }}}_rules_d.results }}" when: item.matched is defined and item.matched == 0 - - name: "Construct rule: add key" + - name: "Construct rule: add fields and key" set_fact: tmpline="{{ tmpline + '{{{ fields_data.list }}} -k {{{ key }}}' }}" - name: "Insert the line in {{ all_files[0] }}" lineinfile: @@ -458,9 +458,9 @@ The macro requires following parameters: - name: Insert the syscall rule in /etc/audit/audit.rules block: - name: "Construct rule: add rule list, action and arch" - set_fact: tmpline="-a always,exit -F arch={{{ arch }}} " + set_fact: tmpline="-a always,exit -F arch={{{ arch }}}" - name: "Construct rule: add syscalls" - set_fact: tmpline="{{tmpline + '-S ' + item.item + ' ' }}" + set_fact: tmpline="{{tmpline + ' -S ' + item.item }}" loop: "{{ audit_syscalls_found_{{{ arch }}}_audit_rules.results }}" when: item.matched is defined and item.matched == 0 - name: "Construct rule: add fields and key" From 80a3b0cca2b3af62e1a7cff578a45e844bd12fb4 Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Thu, 30 Apr 2020 09:10:41 +0200 Subject: [PATCH 3/6] Add tests for audit_rules_time_clock_settime --- .../tests/correct_syscall.pass.sh | 7 +++++++ .../tests/incorrect_arg_field.fail.sh | 7 +++++++ .../tests/incorrect_syscall.fail.sh | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/correct_syscall.pass.sh create mode 100644 linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_arg_field.fail.sh create mode 100644 linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_syscall.fail.sh diff --git a/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/correct_syscall.pass.sh b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/correct_syscall.pass.sh new file mode 100644 index 0000000000..b71cc454bc --- /dev/null +++ b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/correct_syscall.pass.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# profiles = xccdf_org.ssgproject.content_profile_cis + +rm -rf /etc/audit/rules.d/*.rules +echo "-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -k time-change" >> /etc/audit/rules.d/time.rules +echo "-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -k time-change" >> /etc/audit/rules.d/time.rules diff --git a/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_arg_field.fail.sh b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_arg_field.fail.sh new file mode 100644 index 0000000000..add0722747 --- /dev/null +++ b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_arg_field.fail.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# profiles = xccdf_org.ssgproject.content_profile_cis + +rm -rf /etc/audit/rules.d/*.rules +echo "-a always,exit -F arch=b32 -S clock_settime -F a0=0x1 -k time-change" >> /etc/audit/rules.d/time.rules +echo "-a always,exit -F arch=b64 -S clock_settime -F a0=0x1 -k time-change" >> /etc/audit/rules.d/time.rules diff --git a/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_syscall.fail.sh b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_syscall.fail.sh new file mode 100644 index 0000000000..9ab5cc3bc4 --- /dev/null +++ b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/tests/incorrect_syscall.fail.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# profiles = xccdf_org.ssgproject.content_profile_cis + +rm -rf /etc/audit/rules.d/*.rules +echo "-a always,exit -F arch=b32 -S stime -F a0=0x0 -k time-change" >> /etc/audit/rules.d/time.rules +echo "-a always,exit -F arch=b64 -S stime -F a0=0x0 -k time-change" >> /etc/audit/rules.d/time.rules From a5b36f8400f821e35fc5a7e77b36a9fee0124702 Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Thu, 30 Apr 2020 09:34:35 +0200 Subject: [PATCH 4/6] Add Ansible for audit syscall clock_settime Also demonstrates how to use the fields parameter in ansible audit syscall macro. --- .../ansible/shared.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/ansible/shared.yml diff --git a/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/ansible/shared.yml b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/ansible/shared.yml new file mode 100644 index 0000000000..e77850fa25 --- /dev/null +++ b/linux_os/guide/system/auditing/auditd_configure_rules/audit_time_rules/audit_rules_time_clock_settime/ansible/shared.yml @@ -0,0 +1,22 @@ +# platform = multi_platform_all +# reboot = false +# strategy = restrict +# complexity = low +# disruption = low + +# What architecture are we on? +# +- name: Set architecture for audit tasks + set_fact: + audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}" + +- name: Perform remediation of Audit rules for clock_settime for x86 platform + block: + {{{ ansible_audit_augenrules_add_syscall_rule(arch="b32", syscalls=["clock_settime"], key="time-change", fields=["a0=0x0"])|indent(4) }}} + {{{ ansible_audit_auditctl_add_syscall_rule(arch="b32", syscalls=["clock_settime"], key="time-change", fields=["a0=0x0"])|indent(4) }}} + +- name: Perform remediation of Audit rules for clock_settime for x86_64 platform + block: + {{{ ansible_audit_augenrules_add_syscall_rule(arch="b64", syscalls=["clock_settime"], key="time-change", fields=["a0=0x0"])|indent(4) }}} + {{{ ansible_audit_auditctl_add_syscall_rule(arch="b64", syscalls=["clock_settime"], key="time-change", fields=["a0=0x0"])|indent(4) }}} + when: audit_arch == "b64" From fe179d4d870878d29b603e7ab5a8bc79cb8eb05c Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Thu, 30 Apr 2020 11:54:03 +0200 Subject: [PATCH 5/6] Fix regex spacing between fields and the key There needs to be a space between them. Change syntax to be consistent with rest of regex. --- shared/macros-ansible.jinja | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja index 2aaf0c366b..eeafe5f6d5 100644 --- a/shared/macros-ansible.jinja +++ b/shared/macros-ansible.jinja @@ -367,7 +367,7 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul {{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} {{% set fields_data = { 'regex' : "", 'list': "" } %}} {{% for field in fields %}} - {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F\s+' + field + ')'}) %}} + {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F[\s]+' + field + '[\s]+)'}) %}} {{% set not_used = fields_data.update({'list': fields_data.list+ ' -F ' + field }) %}} {{% endfor %}} @@ -440,7 +440,7 @@ The macro requires following parameters: {{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} {{% set fields_data = { 'regex' : "", 'list': "" } %}} {{% for field in fields %}} - {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F\s+' + field + ')'}) %}} + {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F[\s]+' + field + '[\s]+)'}) %}} {{% set not_used = fields_data.update({'list': fields_data.list + ' -F ' + field }) %}} {{% endfor %}} From 5e13b1a6698d4403cf4108664fd2c33be5ee9109 Mon Sep 17 00:00:00 2001 From: Watson Sato Date: Thu, 30 Apr 2020 14:41:59 +0200 Subject: [PATCH 6/6] Improve macro documenation and clarify var name --- shared/macros-ansible.jinja | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja index eeafe5f6d5..7b64341fb7 100644 --- a/shared/macros-ansible.jinja +++ b/shared/macros-ansible.jinja @@ -364,11 +364,14 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul - name: Declare number of syscalls set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}" -{{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} -{{% set fields_data = { 'regex' : "", 'list': "" } %}} +{{# +This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope. +See official documentation: https://jinja.palletsprojects.com/en/2.11.x/templates/#assignments +#}} +{{% set fields_data = { 'regex' : "", 'plain_text': "" } %}} {{% for field in fields %}} {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F[\s]+' + field + '[\s]+)'}) %}} - {{% set not_used = fields_data.update({'list': fields_data.list+ ' -F ' + field }) %}} + {{% set not_used = fields_data.update({'plain_text': fields_data.plain_text + ' -F ' + field }) %}} {{% endfor %}} - name: Check existence of syscalls for architecture {{{ arch }}} in /etc/audit/rules.d/ @@ -410,7 +413,7 @@ Note that if there already exists a rule wit the same key in the /etc/audit/rul loop: "{{ audit_syscalls_found_{{{ arch }}}_rules_d.results }}" when: item.matched is defined and item.matched == 0 - name: "Construct rule: add fields and key" - set_fact: tmpline="{{ tmpline + '{{{ fields_data.list }}} -k {{{ key }}}' }}" + set_fact: tmpline="{{ tmpline + '{{{ fields_data.plain_text }}} -k {{{ key }}}' }}" - name: "Insert the line in {{ all_files[0] }}" lineinfile: path: "{{ all_files[0] }}" @@ -437,11 +440,14 @@ The macro requires following parameters: - name: Declare number of syscalls set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}" -{{# This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope #}} -{{% set fields_data = { 'regex' : "", 'list': "" } %}} +{{# +This dictionary is a Jinja2 trick to allow loops to change variables defined out of its scope. +See official documentation: https://jinja.palletsprojects.com/en/2.11.x/templates/#assignments +#}} +{{% set fields_data = { 'regex' : "", 'plain_text': "" } %}} {{% for field in fields %}} {{% set not_used = fields_data.update({'regex': fields_data.regex + '(?:-F[\s]+' + field + '[\s]+)'}) %}} - {{% set not_used = fields_data.update({'list': fields_data.list + ' -F ' + field }) %}} + {{% set not_used = fields_data.update({'plain_text': fields_data.plain_text + ' -F ' + field }) %}} {{% endfor %}} - name: Check existence of syscalls for architecture {{{ arch }}} in /etc/audit/audit.rules @@ -464,7 +470,7 @@ The macro requires following parameters: loop: "{{ audit_syscalls_found_{{{ arch }}}_audit_rules.results }}" when: item.matched is defined and item.matched == 0 - name: "Construct rule: add fields and key" - set_fact: tmpline="{{ tmpline + '{{{ fields_data.list }}} -k {{{ key }}}' }}" + set_fact: tmpline="{{ tmpline + '{{{ fields_data.plain_text }}} -k {{{ key }}}' }}" - name: Insert the line in /etc/audit/audit.rules lineinfile: path: "/etc/audit/audit.rules"