mirror of
https://github.com/inspec/inspec
synced 2024-09-21 15:02:01 +00:00
aide_conf resource: test configuration of the AIDE file integrity tool (#2063)
* Added aide_conf resource and subsequent files * Updated to match on all selection lines Signed-off-by: Jennifer Burns <jburns@mitre.org> * Changed to use CommentParser and fixed typo Signed-off-by: Jennifer Burns <jburns@mitre.org> * Fix typo in test file Signed-off-by: Jennifer Burns <jburns@mitre.org> * Updated to address PR feedback Signed-off-by: Jennifer Burns <jburns@mitre.org>
This commit is contained in:
parent
f89ddcc832
commit
2cef15aec3
6 changed files with 298 additions and 0 deletions
81
docs/resources/aide_conf.md.erb
Normal file
81
docs/resources/aide_conf.md.erb
Normal file
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
title: About the aide_conf Resource
|
||||
---
|
||||
|
||||
# aide_conf
|
||||
|
||||
Use the `aide_conf` InSpec audit resource to test the rules established for the file integrity tool AIDE. Controlled by the aide.conf file typically at /etc/aide.conf.
|
||||
|
||||
## Syntax
|
||||
|
||||
An `aide_conf` resource block can be used to determine if the selection lines contain one (or more) directories whose files should be added to the aide database:
|
||||
|
||||
describe aide_conf('path') do
|
||||
its('selection_lines') { should include '/sbin' }
|
||||
end
|
||||
|
||||
where
|
||||
|
||||
* `'selection_lines'` refers to all selection lines found in the aide.conf file
|
||||
* `('path')` is the non-default path to the `aide.conf` file (optional)
|
||||
* `should include 'value'` is the value that is expected
|
||||
|
||||
Use the where clause to match a selection_line to one rule or a particular set of rules found in the aide.conf file:
|
||||
|
||||
describe aide_conf.where { selection_line == '/bin' } do
|
||||
its('rules.flatten') { should include 'r' }
|
||||
end
|
||||
|
||||
describe aide_conf.where { selection_line == '/sbin' } do
|
||||
its('rules') { should include ['p', 'i', 'l', 'n', 'u', 'g', 'sha512'] }
|
||||
end
|
||||
|
||||
## Matchers
|
||||
|
||||
This InSpec audit resource has the following matchers:
|
||||
|
||||
### be
|
||||
|
||||
<%= partial "/shared/matcher_be" %>
|
||||
|
||||
### cmp
|
||||
|
||||
<%= partial "/shared/matcher_cmp" %>
|
||||
|
||||
### eq
|
||||
|
||||
<%= partial "/shared/matcher_eq" %>
|
||||
|
||||
### include
|
||||
|
||||
<%= partial "/shared/matcher_include" %>
|
||||
|
||||
### all_have_rule
|
||||
|
||||
The usage of all_have_rule will return whether or not all selection lines in audit.conf contain a particular rule:
|
||||
|
||||
describe aide_conf.all_have_rule('sha512') do
|
||||
it { should eq true }
|
||||
end
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this InSpec audit resource.
|
||||
|
||||
### Test if all selection lines contain the xattr rule
|
||||
|
||||
describe aide_conf.all_have_rule('xattr') do
|
||||
it { should eq true }
|
||||
end
|
||||
|
||||
### Test whether selection line for /bin contains a particular rule
|
||||
|
||||
describe aide_conf.where { selection_line == '/bin' } do
|
||||
its('rules.flatten') { should include 'r' }
|
||||
end
|
||||
|
||||
### Test whether selection line for /sbin consists of a particular set of rules
|
||||
|
||||
describe aide_conf.where { selection_line == '/sbin' } do
|
||||
its('rules') { should include ['r', 'sha512'] }
|
||||
end
|
|
@ -72,6 +72,7 @@ module Inspec
|
|||
end
|
||||
end
|
||||
|
||||
require 'resources/aide_conf'
|
||||
require 'resources/apache'
|
||||
require 'resources/apache_conf'
|
||||
require 'resources/apt'
|
||||
|
|
162
lib/resources/aide_conf.rb
Normal file
162
lib/resources/aide_conf.rb
Normal file
|
@ -0,0 +1,162 @@
|
|||
# encoding: utf-8
|
||||
# author: Jen Burns, burnsjennifere@gmail.com
|
||||
|
||||
require 'utils/filter'
|
||||
require 'utils/parser'
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module Inspec::Resources
|
||||
class AideConf < Inspec.resource(1)
|
||||
name 'aide_conf'
|
||||
desc 'Use the aide_conf InSpec audit resource to test the rules established for
|
||||
the file integrity tool AIDE. Controlled by the aide.conf file typically at /etc/aide.conf.'
|
||||
example "
|
||||
describe aide_conf do
|
||||
its('selection_lines') { should include '/sbin' }
|
||||
end
|
||||
|
||||
describe aide_conf.where { selection_line == '/bin' } do
|
||||
its('rules.flatten') { should include 'r' }
|
||||
end
|
||||
|
||||
describe aide_conf.all_have_rule('sha512') do
|
||||
it { should eq true }
|
||||
end
|
||||
"
|
||||
|
||||
attr_reader :params
|
||||
|
||||
include CommentParser
|
||||
|
||||
def initialize(aide_conf_path = nil)
|
||||
return skip_resource 'The `aide_conf` resource is not supported on your OS.' unless inspec.os.linux?
|
||||
@conf_path = aide_conf_path || '/etc/aide.conf'
|
||||
@content = nil
|
||||
@rules = nil
|
||||
read_content
|
||||
end
|
||||
|
||||
def all_have_rule(rule)
|
||||
# Case when file didn't exist or perms didn't allow an open
|
||||
return false if @content.nil?
|
||||
|
||||
lines = @params.reject { |line| line['rules'].include? rule }
|
||||
lines.empty?
|
||||
end
|
||||
|
||||
filter = FilterTable.create
|
||||
filter.add_accessor(:where)
|
||||
.add_accessor(:entries)
|
||||
.add(:selection_lines, field: 'selection_line')
|
||||
.add(:rules, field: 'rules')
|
||||
|
||||
filter.connect(self, :params)
|
||||
|
||||
private
|
||||
|
||||
def read_content
|
||||
return @content unless @content.nil?
|
||||
@rules = {}
|
||||
|
||||
file = inspec.file(@conf_path)
|
||||
if !file.file?
|
||||
return skip_resource "Can't find file \"#{@conf_path}\""
|
||||
end
|
||||
raw_conf = file.content
|
||||
if raw_conf.nil?
|
||||
return skip_resource "File can't be opened or is empty \"#{@conf_path}\""
|
||||
end
|
||||
if raw_conf.empty? && !file.empty?
|
||||
return skip_resource "Can't read file \"#{@conf_path}\""
|
||||
end
|
||||
|
||||
# If there is a file and it contains content, continue
|
||||
@content = filter_comments(inspec.file(@conf_path).content.lines)
|
||||
@params = parse_conf(@content)
|
||||
end
|
||||
|
||||
def filter_comments(data)
|
||||
content = []
|
||||
data.each do |line|
|
||||
content_line, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
|
||||
content.push(content_line)
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
def parse_conf(content)
|
||||
params = []
|
||||
content.each do |line|
|
||||
param = parse_line(line)
|
||||
if !param['selection_line'].nil?
|
||||
params.push(param)
|
||||
end
|
||||
end
|
||||
params
|
||||
end
|
||||
|
||||
def parse_line(line)
|
||||
line_and_rules = {}
|
||||
# Case when line is a rule line
|
||||
if line.include?(' = ')
|
||||
parse_rule_line(line)
|
||||
# Case when line is a selection line
|
||||
elsif line.start_with?('/', '!', '=')
|
||||
line_and_rules = parse_selection_line(line)
|
||||
end
|
||||
line_and_rules
|
||||
end
|
||||
|
||||
def parse_rule_line(line)
|
||||
line.gsub!(/\s+/, '')
|
||||
rule_line_arr = line.split('=')
|
||||
rules_list = rule_line_arr.last.split('+')
|
||||
rule_name = rule_line_arr.first
|
||||
rules_list.each_index do |i|
|
||||
# Cases where rule respresents one or more other rules
|
||||
if @rules.key?(rules_list[i])
|
||||
rules_list[i] = @rules[rules_list[i]]
|
||||
end
|
||||
rules_list[i] = handle_multi_rule(rules_list, i)
|
||||
end
|
||||
@rules[rule_name] = rules_list.flatten
|
||||
end
|
||||
|
||||
def parse_selection_line(line)
|
||||
selec_line_arr = line.split(' ')
|
||||
selection_line = selec_line_arr.first
|
||||
selection_line.chop! if selection_line.end_with?('/')
|
||||
rule_list = selec_line_arr.last.split('+')
|
||||
rule_list.each_index do |i|
|
||||
hash_list = @rules[rule_list[i]]
|
||||
# Cases where rule respresents one or more other rules
|
||||
if !hash_list.nil?
|
||||
rule_list[i] = hash_list
|
||||
end
|
||||
rule_list[i] = handle_multi_rule(rule_list, i)
|
||||
end
|
||||
rule_list.flatten!
|
||||
{
|
||||
'selection_line' => selection_line,
|
||||
'rules' => rule_list,
|
||||
}
|
||||
end
|
||||
|
||||
def handle_multi_rule(rule_list, i)
|
||||
# Rules that represent multiple rules (R,L,>)
|
||||
r_rules = %w{p i l n u g s m c md5}
|
||||
l_rules = %w{p i l n u g}
|
||||
grow_log_rules = %w{p l u g i n S}
|
||||
|
||||
case rule_list[i]
|
||||
when 'R'
|
||||
return r_rules
|
||||
when 'L'
|
||||
return l_rules
|
||||
when '>'
|
||||
return grow_log_rules
|
||||
end
|
||||
rule_list[i]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -158,6 +158,7 @@ class MockLoader
|
|||
'C:/etc/postgresql/9.5/main/pg_ident.conf' => mockfile.call('pg_ident.conf'),
|
||||
'/etc/postgresql/9.5/main' => mockfile.call('9.5.main'),
|
||||
'/var/lib/postgresql/9.5/main' => mockfile.call('var.9.5.main'),
|
||||
'/etc/aide.conf' => mockfile.call('aide.conf'),
|
||||
'/var/lib/fake_rpmdb' => mockdir.call(true),
|
||||
'/var/lib/rpmdb_does_not_exist' => mockdir.call(false),
|
||||
}
|
||||
|
|
18
test/unit/mock/files/aide.conf
Normal file
18
test/unit/mock/files/aide.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
# This file contains a contrived aide.conf file to use for testing
|
||||
@@define DBDIR /var/lib/aide
|
||||
|
||||
# The location of the database
|
||||
database_out=file:etc
|
||||
|
||||
gzip_dbout=yes
|
||||
|
||||
#Default
|
||||
verbose=5
|
||||
|
||||
ALLTEST = R+sha512
|
||||
ALLXTRAHASHES = b+t+ALLTEST
|
||||
|
||||
/boot/ ALLTEST
|
||||
/bin ALLXTRAHASHES
|
||||
/sbin ALLTEST
|
||||
/etc/hosts ALLTEST
|
35
test/unit/resources/aide_conf_test.rb
Normal file
35
test/unit/resources/aide_conf_test.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# encoding: utf-8
|
||||
# author: Jen Burns, burnsjennifere@gmail.com
|
||||
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
|
||||
describe 'Inspec::Resources::AideConf' do
|
||||
describe 'AideConf Parameters' do
|
||||
resource = load_resource('aide_conf')
|
||||
it 'Verify aide_conf all_have_rule property - true case' do
|
||||
_(resource.all_have_rule('p')).must_equal true
|
||||
end
|
||||
it 'Verify aide_conf all_have_rule property - false case' do
|
||||
_(resource.all_have_rule('x')).must_equal false
|
||||
end
|
||||
it 'Verify aide_conf filtering by selection_line for single rule' do
|
||||
entries = resource.where { selection_line == '/bin' }
|
||||
_(entries.rules.flatten).must_include 'sha512'
|
||||
end
|
||||
it 'Verify handle_multi_rule properly expands rules based on macro' do
|
||||
entries = resource.where { selection_line == '/sbin' }
|
||||
_(entries.rules).must_include %w{p i l n u g s m c md5 sha512}
|
||||
end
|
||||
it 'Verify parse_rule_line properly expands rules based on macro' do
|
||||
entries = resource.where { selection_line == '/bin' }
|
||||
_(entries.rules).must_include %w{b t p i l n u g s m c md5 sha512}
|
||||
end
|
||||
it 'Verify parse_selection_line normalizes directories ending in /' do
|
||||
_(resource.selection_lines).must_include '/boot'
|
||||
end
|
||||
it 'Verify aide_conf finds all selection_line dirs' do
|
||||
_(resource.selection_lines).must_equal ['/boot', '/bin', '/sbin', '/etc/hosts']
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue