etc_fstab resource: test contents of the /etc/fstab file (#2064)

* Adding support for fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* New Resource - etc_fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* New Resource - etc_fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* Modifications to new resource - etc_fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* Modifications to new resource - etc_fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* Modifications to new resource - etc_fstab

Signed-off-by: dromazos <dromazmj@dukes.jmu.edu>

* Modifications to docs of new resource etc_fstab

Signed-off-by: dromazmj <dromazmj@dukes.jmu.edu>

* Modifications to new resource etc_fstab

Signed-off-by: dromazmj <dromazmj@dukes.jmu.edu>
This commit is contained in:
dromazmj 2017-09-11 09:55:03 -04:00 committed by Dominik Richter
parent a9d0d65c54
commit 70548ab754
8 changed files with 335 additions and 0 deletions

View file

@ -0,0 +1,117 @@
---
title: About the etc_fstab Resource
---
# etc_fstab
Use the `etc_fstab` InSpec audit resource to test information about all partitions and storage devices on a system.
## Syntax
An etc_fstab rule specifies a device name, its mount point, its mount type, the options its mounted with,
its dump options, and the order the files system should be checked.
## Syntax
Use the where clause to match a property to one or more rules in the fstab file.
describe etc_fstab.where { device_name == 'value' } do
its('mount_point') { should cmp 'hostname' }
its('file_system_type') { should cmp 'list' }
its('mount_options') { should cmp 'list' }
its('dump_options') { should cmp 'list' }
its('file_system_options') { should cmp 'list' }
end
Use the optional constructor parameter to give an alternative path to fstab file
describe etc_fstab(hosts_path).where { device_name == 'value' } do
its('mount_point') { should cmp 'hostname' }
its('file_system_type') { should cmp 'list' }
its('mount_options') { should cmp 'list' }
its('dump_options') { should cmp 'list' }
its('file_system_options') { should cmp 'list ' }
end
where
* `device_name` is the name associated with the device.
* `mount_point` is the directory at which the filesystem is configured to be mounted.
* `file_system_type` is the type of file system of the device or partition.
* `mount_options` is the options for the device or partition.
* `dump_options` is a number used by dump to decide if a file system should be backed up.
* `file_system_options` is a number that specifies the order the file system should be checked.
## Property Examples and Return Types
### device_name
`device_name` returns a string array of device names mounted on the system.
describe etc_fstab.where { mount_point == '/mnt/sr0' } do
its('device_name') { should cmp '/dev/sr0' }
end
### mount_point
`mount_point` returns a string array of directorys at which filesystems are configured to be mounted.
describe etc_fstab.where { device_name == '/dev/sr0' } do
its('mount_point') { should cmp '/mnt/sr0' }
end
### file_system_type
`file_system_type` returns a String array of each partitions file system type.
describe etc_fstab.where { device_name == '/dev/sr0' } do
its('file_system_type') { should cmp 'iso9660' }
end
### mount_options
`mount_options` returns a two dimensional array of each partitions mount options.
describe etc_fstab.where { mount_point == '/' } do
its('mount_options') { should eq [['defaults', 'x-systemd.device-timeout=0']] }
end
### dump_options
`dump_options` returns a integer array of each partitions dump option.
describe etc_fstab.where { device_name == '/dev/sr0' } do
its('dump_options') { should cmp 0 }
end
### file_system_options
`file_system_options` returns a integer array of each partitions file system option.
describe etc_fstab.where { device_name == '/dev/sr0' } do
its('file_system_options') { should cmp 0 }
end
## Examples
The following examples show how to use this InSpec resource.
### Check all partitions that have type of 'nfs'.
nfs_systems = etc_fstab.nfs_file_systems
nfs_systems.each do |partition|
describe partition do
its('mount_options') { should include 'nosuid' }
end
end
### Check the partition mounted at /home contains 'nosuid' in its mount_options.
describe etc_fstab do
its('home_mount_options') { should include 'nosuid' }
end
### Check if a partition is mounted at a point.
describe etc_fstab.where { mount_point == '/home' } do
it { should be_configured }
end

View file

@ -89,6 +89,7 @@ require 'resources/directory'
require 'resources/docker'
require 'resources/docker_image'
require 'resources/docker_container'
require 'resources/etc_fstab'
require 'resources/etc_group'
require 'resources/etc_hosts'
require 'resources/file'

107
lib/resources/etc_fstab.rb Normal file
View file

@ -0,0 +1,107 @@
# encoding: utf-8
# copyright:
# author: Matthew Dromazos
require 'utils/parser'
class EtcFstab < Inspec.resource(1)
name 'etc_fstab'
desc 'Use the etc_fstab InSpec audit resource to check the configuration of the etc/fstab file.'
example "
removable_media = etc_fstab.removable_media_file_systems
removable_media.each do |media|
describe media do
its ('mount_options') { should include 'nosuid' }
end
end
nfs_systems = etc_fstab.nfs_file_systems
nfs_systems.each do |file_system|
describe file_system do
its ('mount_options') { should include 'nosuid' }
its ('mount_options') { should include 'noexec' }
its ('mount_options') { should include '\'sec=krb5:krb5i:krb5p\'' }
end
end
describe etc_fstab do
its ('home_mount_options') { should include 'nosuid' }
end
"
attr_reader :params
include CommentParser
def initialize(fstab_path = nil)
return skip_resource 'The `etc_fstab` resource is not supported on your OS.' unless inspec.os.linux?
@conf_path = fstab_path || '/etc/fstab'
@files_contents = {}
@content = nil
@params = nil
read_content
end
filter = FilterTable.create
filter.add_accessor(:where)
.add_accessor(:entries)
.add(:device_name, field: 'device_name')
.add(:mount_point, field: 'mount_point')
.add(:file_system_type, field: 'file_system_type')
.add(:mount_options, field: 'mount_options')
.add(:dump_options, field: 'dump_options')
.add(:file_system_options, field: 'file_system_options')
.add(:configured?) { |x| x.entries.any? }
filter.connect(self, :params)
def nfs_file_systems
where { file_system_type.match(/nfs/) }
end
def home_mount_options
return nil unless where { mount_point == '/home' }.configured?
where { mount_point == '/home' }.entries[0].mount_options
end
private
def read_content
@content = ''
@params = {}
@content = read_file(@conf_path)
@params = parse_conf(@content)
end
def parse_conf(content)
content.map do |line|
data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
parse_line(data) unless data == ''
end.compact
end
def parse_line(line)
attributes = line.split
{
'device_name' => attributes[0],
'mount_point' => attributes[1],
'file_system_type' => attributes[2],
'mount_options' => attributes[3].split(','),
'dump_options' => attributes[4].to_i,
'file_system_options' => attributes[5].to_i,
}
end
def read_file(conf_path = @conf_path)
file = inspec.file(conf_path)
if !file.file?
return skip_resource "Can't find \"#{@conf_path}\""
end
raw_conf = file.content
if raw_conf.empty? && !file.empty?
return skip_resource("File is empty or unable to read file at path:\"#{@conf_path}\"")
end
raw_conf.lines
end
end

View file

@ -161,6 +161,9 @@ class MockLoader
'/var/lib/postgresql/9.5/main' => mockfile.call('var.9.5.main'),
'/etc/hosts' => mockfile.call('hosts'),
'C:\windows\system32\drivers\etc\hosts' => mockfile.call('hosts'),
'/etc/fstab' => mockfile.call('fstab'),
'fstab_no_home' => mockfile.call('fstab_no_home'),
'fstab_one_mount' => mockfile.call('fstab_one_mount'),
'/etc/aide.conf' => mockfile.call('aide.conf'),
'/var/lib/fake_rpmdb' => mockdir.call(true),
'/var/lib/rpmdb_does_not_exist' => mockdir.call(false),

View file

@ -0,0 +1,19 @@
#
# /etc/fstab
# Created by anaconda on Fri Jul 21 21:25:41 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/vg1-lv_root / xfs defaults,x-systemd.device-timeout=0 0 0
UUID=ebffcd01-0695-4d4a-91a4-3a53c6f88d06 /boot xfs defaults,nodev,nosuid 0 0
/dev/mapper/vg1-lv_home /home xfs defaults,x-systemd.device-timeout=0,nodev,nosuid 0 0
/dev/mapper/vg1-lv_tmp /tmp xfs defaults,x-systemd.device-timeout=0,nodev,noexec,nosuid 0 0
/dev/mapper/vg1-lv_var /var xfs defaults,x-systemd.device-timeout=0,nodev,nosuid 0 0
/dev/mapper/vg1-lv_log /var/log xfs defaults,x-systemd.device-timeout=0,nodev,nosuid 0 0
/dev/mapper/vg1-lv_audit /var/log/audit xfs defaults,x-systemd.device-timeout=0,nodev,nosuid,nodev,noexec,nosuid 0 0
/dev/mapper/vg1-lv_swap swap swap defaults,x-systemd.device-timeout=0 0 0
/dev/sr0 /mnt/sr0 iso9660 defaults,ro,noexec,noauto 0 0
/dev/cdrom /mnt/cdrom iso9660 defaults,ro,noexec,noauto 0 0
tmpfs /dev/shm tmpfs noexec,nosuid,nodev 0 0
server:/usr/local/pub /pub nfs rsize=8192,wsize=8192,timeo=14,intr 0 0

View file

@ -0,0 +1,18 @@
#
# /etc/fstab
# Created by anaconda on Fri Jul 21 21:25:41 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/vg1-lv_root / xfs defaults,x-systemd.device-timeout=0 0 0
UUID=ebffcd01-0695-4d4a-91a4-3a53c6f88d06 /boot xfs defaults,nodev,nosuid 0 0
/dev/mapper/vg1-lv_tmp /tmp xfs defaults,x-systemd.device-timeout=0,nodev,noexec,nosuid 0 0
/dev/mapper/vg1-lv_var /var xfs defaults,x-systemd.device-timeout=0,nodev,nosuid 0 0
/dev/mapper/vg1-lv_log /var/log xfs defaults,x-systemd.device-timeout=0,nodev,nosuid 0 0
/dev/mapper/vg1-lv_audit /var/log/audit xfs defaults,x-systemd.device-timeout=0,nodev,nosuid,nodev,noexec,nosuid 0 0
/dev/mapper/vg1-lv_swap swap swap defaults,x-systemd.device-timeout=0 0 0
/dev/sr0 /mnt/sr0 iso9660 defaults,ro,noexec,noauto 0 0
/dev/cdrom /mnt/cdrom iso9660 defaults,ro,noexec,noauto 0 0
tmpfs /dev/shm tmpfs noexec,nosuid,nodev 0 0
server:/usr/local/pub /pub nfs rsize=8192,wsize=8192,timeo=14,intr 0 0

View file

@ -0,0 +1,8 @@
#
# /etc/fstab
# Created by anaconda on Fri Jul 21 21:25:41 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/vg1-lv_root / xfs defaults,x-systemd.device-timeout=0 0 0

View file

@ -0,0 +1,62 @@
# encoding: utf-8
# author: Matthew Dromazos
require 'helper'
require 'inspec/resource'
describe 'Inspec::Resources::Fstab' do
let(:resource) { load_resource('etc_fstab') }
it 'Verify etc_hosts filtering by `device_name`' do
entries = resource.where { device_name == '/dev/mapper/vg1-lv_root' }
_(entries.mount_point).must_equal ['/']
_(entries.file_system_type).must_equal ['xfs']
_(entries.mount_options).must_equal [['defaults', 'x-systemd.device-timeout=0']]
_(entries.dump_options).must_equal [0]
_(entries.file_system_options).must_equal [0]
end
it 'Verify etc_hosts filtering by `mount_point`' do
entries = resource.where { mount_point == '/' }
_(entries.device_name).must_equal ['/dev/mapper/vg1-lv_root']
_(entries.file_system_type).must_equal ['xfs']
_(entries.mount_options).must_equal [['defaults', 'x-systemd.device-timeout=0']]
_(entries.dump_options).must_equal [0]
_(entries.file_system_options).must_equal [0]
end
it 'Verify parsing an entry where mount_options is a single item' do
resourceOneMount = load_resource('etc_fstab', 'fstab_one_mount')
entries = resourceOneMount.where { file_system_options == 0 }
_(entries.mount_options).must_equal [['defaults', 'x-systemd.device-timeout=0']]
end
it 'Verify parsing an entry where mount_options is multiple items' do
entries = resource.where { file_system_options == 0 }
_(entries.mount_options).must_equal [['defaults', 'x-systemd.device-timeout=0'] , ['defaults', 'nodev', 'nosuid'], ['defaults', 'x-systemd.device-timeout=0', 'nodev', 'nosuid'], ['defaults', 'x-systemd.device-timeout=0', 'nodev', 'noexec', 'nosuid'],
['defaults', 'x-systemd.device-timeout=0', 'nodev', 'nosuid'], ['defaults', 'x-systemd.device-timeout=0', 'nodev', 'nosuid'], ['defaults', 'x-systemd.device-timeout=0', 'nodev', 'nosuid', 'nodev', 'noexec', 'nosuid'], ['defaults', 'x-systemd.device-timeout=0'],
['defaults', 'ro', 'noexec', 'noauto'], ['defaults', 'ro', 'noexec', 'noauto'], ['noexec', 'nosuid', 'nodev'], ['rsize=8192', 'wsize=8192', 'timeo=14', 'intr'] ]
end
it 'verify home_mount_options returns something when /home is configured' do
entries = resource.where { mount_point == '/home' }
_(entries.configured?).must_equal true
_(resource.home_mount_options).must_equal [ 'defaults', 'x-systemd.device-timeout=0', 'nodev', 'nosuid']
end
it 'verify home_mount_options returns something when /home is not configured' do
resourceNoHome = load_resource('etc_fstab', 'fstab_no_home')
entries = resourceNoHome.where { mount_point == '/home' }
_(entries.configured?).must_equal false
_(resourceNoHome.home_mount_options).must_be_nil
end
it 'verify etc_fstab can detect all nfs file systems' do
entries = resource.nfs_file_systems
_(entries.device_name).must_equal ['server:/usr/local/pub']
_(entries.mount_point).must_equal ['/pub']
_(entries.file_system_type).must_equal ['nfs']
_(entries.mount_options).must_equal [['rsize=8192', 'wsize=8192', 'timeo=14', 'intr']]
_(entries.dump_options).must_equal [0]
_(entries.file_system_options).must_equal [0]
end
end