-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: New spec and parser for"auditctl -l" #3496
Merged
+256
−0
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.. automodule:: insights.parsers.auditctl | ||
:members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
""" | ||
AuditCtl - command ``auditctl xxx`` | ||
=================================== | ||
|
||
This module contains the following parsers: | ||
|
||
AuditRules - command ``auditctl -l`` | ||
------------------------------------ | ||
AuditStatus - command ``auditctl -s`` | ||
------------------------------------- | ||
|
||
""" | ||
|
||
from insights import parser, CommandParser | ||
from insights.parsers import ParseException, SkipException | ||
from insights.specs import Specs | ||
|
||
|
||
@parser(Specs.auditctl_rules) | ||
class AuditRules(CommandParser, list): | ||
""" | ||
Class for parsing the `auditctl -l` command. | ||
All lines are stored in a list. | ||
|
||
Typical output of the command is:: | ||
|
||
-w /etc/selinux -p wa -k MAC-policy | ||
-a always,exit -F arch=b32 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b32 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b32 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b32 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
|
||
Examples: | ||
>>> type(audit_rules) | ||
<class 'insights.parsers.auditctl.AuditRules'> | ||
>>> len(audit_rules) | ||
9 | ||
>>> '-w /etc/selinux -p wa -k MAC-policy' in audit_rules | ||
True | ||
|
||
Raises: | ||
SkipException: When there are not rules. | ||
""" | ||
|
||
def parse_content(self, content): | ||
if len(content) == 1 and content[0].lower().strip() == 'no rules': | ||
raise SkipException | ||
for line in content: | ||
if line.strip(): | ||
self.append(line.strip()) | ||
if not self: | ||
raise SkipException('No rules found') | ||
|
||
|
||
@parser(Specs.auditctl_status) | ||
class AuditStatus(CommandParser, dict): | ||
""" | ||
Module for parsing the output of the ``auditctl -s`` command. | ||
|
||
Typical output on RHEL6 looks like:: | ||
|
||
AUDIT_STATUS: enabled=1 flag=1 pid=1483 rate_limit=0 backlog_limit=8192 lost=3 backlog=0 | ||
|
||
, while on RHEL7 and later, the output changes to:: | ||
|
||
enabled 1 | ||
failure 1 | ||
pid 947 | ||
rate_limit 0 | ||
backlog_limit 320 | ||
lost 0 | ||
backlog 0 | ||
loginuid_immutable 0 unlocked | ||
|
||
Example: | ||
>>> type(auds) | ||
<class 'insights.parsers.auditctl.AuditStatus'> | ||
>>> "enabled" in auds | ||
True | ||
>>> auds['enabled'] | ||
1 | ||
""" | ||
def parse_content(self, content): | ||
if not content: | ||
raise SkipException("Input content is empty.") | ||
if len(content) > 1: | ||
for line in content: | ||
k, v = line.split(None, 1) | ||
# Mind the 'loginuid_immutable' on RHEL7 | ||
if k.strip() == "loginuid_immutable": | ||
self[k.strip()] = v.strip() | ||
else: | ||
try: | ||
self[k.strip()] = int(v.strip()) | ||
except ValueError: | ||
raise ParseException('Unexpected type in line %s' % line) | ||
if len(content) == 1: | ||
line = list(content)[0].strip() | ||
if line.startswith("AUDIT_STATUS:"): | ||
for item in line.split(None)[1:]: | ||
try: | ||
k, v = item.split('=') | ||
self[k.strip()] = int(v.strip()) | ||
except ValueError: | ||
raise ParseException('Unexpected type in line %s ' % line) | ||
if not self: | ||
raise SkipException('There is no content in the status output.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import pytest | ||
import doctest | ||
|
||
from insights.tests import context_wrap | ||
from insights.parsers import auditctl | ||
from insights.parsers.auditctl import AuditStatus, AuditRules | ||
from insights.parsers import ParseException, SkipException | ||
|
||
|
||
NORMAL_AUDS_RHEL6 = """ | ||
AUDIT_STATUS: enabled=1 flag=1 pid=1483 rate_limit=0 backlog_limit=8192 lost=3 backlog=0 | ||
""".strip() | ||
|
||
BAD_AUDS_RHEL6 = """ | ||
AUDIT_STATUS: enabled=1 flag=1 pid=1483 rate_limit=0 backlog_limit=8192 lost=3 backlog=0 test=test | ||
""".strip() | ||
|
||
NORMAL_AUDS_RHEL7 = """ | ||
enabled 1 | ||
failure 1 | ||
pid 947 | ||
rate_limit 0 | ||
backlog_limit 320 | ||
lost 0 | ||
backlog 0 | ||
loginuid_immutable 1 locked | ||
""".strip() | ||
|
||
BAD_AUDS_RHEL7 = """ | ||
enabled 1 | ||
failure 1 | ||
pid 947 | ||
rate_limit 0 | ||
backlog_limit 320 | ||
lost 0 | ||
backlog 0 | ||
test test | ||
loginuid_immutable 1 locked | ||
""".strip() | ||
|
||
BLANK_INPUT_SAMPLE = """ | ||
""".strip() | ||
|
||
BAD_INPUT_SAMPLE = """ | ||
Unknown: type=0, len=0 | ||
""".strip() | ||
|
||
BAD_INPUT_MIX = """ | ||
Unknown: type=0, len=0 | ||
enabled 1 | ||
""".strip() | ||
|
||
AUDIT_RULES_OUTPUT1 = """ | ||
No rules | ||
""".strip() | ||
|
||
AUDIT_RULES_OUTPUT2 = """ | ||
-w /etc/selinux -p wa -k MAC-policy | ||
-a always,exit -F arch=b32 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chmod -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b32 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chmod -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
|
||
-a always,exit -F arch=b32 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chown -F exit=-EACCES -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b32 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
-a always,exit -F arch=b64 -S chown -F exit=-EPERM -F auid>=1000 -F auid!=-1 -F key=access | ||
""".strip() | ||
|
||
AUDIT_RULES_OUTPUT3 = """ | ||
|
||
""" | ||
|
||
|
||
def test_normal_auds_rhel6(): | ||
auds = AuditStatus(context_wrap(NORMAL_AUDS_RHEL6)) | ||
assert "enabled" in auds | ||
assert "loginuid_immutable" not in auds | ||
assert auds['pid'] == 1483 | ||
|
||
|
||
def test_normal_auds_rhel7(): | ||
auds = AuditStatus(context_wrap(NORMAL_AUDS_RHEL7)) | ||
assert "loginuid_immutable" in auds | ||
assert auds['loginuid_immutable'] == "1 locked" | ||
assert auds['failure'] == 1 | ||
assert auds.get('nonexists') is None | ||
|
||
|
||
def test_auds_blank_input(): | ||
ctx = context_wrap(BLANK_INPUT_SAMPLE) | ||
with pytest.raises(SkipException) as sc: | ||
AuditStatus(ctx) | ||
assert "Input content is empty." in str(sc) | ||
with pytest.raises(SkipException): | ||
AuditStatus(context_wrap(BAD_INPUT_SAMPLE)) | ||
|
||
|
||
def test_parse_exception(): | ||
with pytest.raises(ParseException): | ||
AuditStatus(context_wrap(BAD_AUDS_RHEL7)) | ||
with pytest.raises(ParseException): | ||
AuditStatus(context_wrap(BAD_AUDS_RHEL6)) | ||
|
||
|
||
def test_audit_rules(): | ||
audit_rules = AuditRules(context_wrap(AUDIT_RULES_OUTPUT2)) | ||
assert len(audit_rules) == 9 | ||
assert '-w /etc/selinux -p wa -k MAC-policy' in audit_rules | ||
|
||
|
||
def test_audit_rules_exception(): | ||
with pytest.raises(SkipException): | ||
AuditRules(context_wrap(AUDIT_RULES_OUTPUT1)) | ||
with pytest.raises(SkipException): | ||
AuditRules(context_wrap(AUDIT_RULES_OUTPUT3)) | ||
|
||
|
||
def test_doc_examples(): | ||
env = { | ||
'audit_rules': AuditRules(context_wrap(AUDIT_RULES_OUTPUT2)), | ||
'auds': AuditStatus(context_wrap(NORMAL_AUDS_RHEL7)) | ||
} | ||
failed, total = doctest.testmod(auditctl, globs=env) | ||
assert failed == 0 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@psachin after discussion with @xiangce , we agree to add a new module to include the parsers about "auditctl" commands, because the original file name is "auditctl_status.py", it's not good to add other "auditctl" commands. And I mark the original one as deprecated. Now the version is wrong, I'm not sure which version I should use, please help here, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3.1.25 is OK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better introduce a global variable, e.g. "NEXT_MINOR_VER", to avoid such kind of confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, the exact version list isn't done, so a few parsers are marked as "3.1.25". Sachin said it is a release which is one year later. After the version list comes out, maybe we'll mark different versions for the parser. Maybe in one release, there is only one parser deprecated. In this case, maybe a global variable is useless? Do we need to do it now for "3.1.25"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the incomplete comment, that is for @psachin, but not a blocker for this PR, please go ahead and use the hard-coded
3.1.25
for this change.No matter how many
component
(even none) would be deprecated in the next minor release version, it would be better to use a global variable than the hard-coded versions, from the development and release perspective.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xiangce By using custom version the developers can plan the feature deprecation accordingly. The developer might not want to deprecate the feature in NEXT_MINOR_VER. I have drafted a change in the docs with examples to help them in this process[1].
[1] #3500