Nova is designed to audit the compliance and security level of an existing system. It is composed of multiple modules, which ingest YAML configuration files to run a series of audits on a system.
Place hubble.py in your _modules/ directory in your Salt
fileserver (whether roots or gitfs) and sync it to the minion.
Create a hubblestack_nova directory in the root of your Salt fileserver's
base environment. Inside of this directory, create a directory tree to
organize your audit modules. Place any desired audit modules into this
directory tree, along with any supporting files (yaml files, etc). Nova audits
are targeted via this directory structure, with an optional filter on tags
The directory/environment in which nova searches for audit modules are configurable via pillar. The defaults are shown below:
hubblestack.nova.dir: salt://hubblestack_nova
hubblestack.nova.saltenv: baseThere are three functions in the hubble.py module. hubble.sync will sync the
configured hubblestack_nova/ directory to the minion. hubble.load will
load the synced audit modules and their yaml configuration files. Finally,
hubble.audit will run the audits.
By default, hubble.audit will call hubble.load (which in turn calls
hubble.sync) (in order to ensure that it is auditing with the most up-to-date
information. These operations are fairly fast, but if you want to avoid the
additional overhead, you can disable these behaviors via pillar (defaults are
shown, change to False to disable behaviors):
hubblestack.nova.autosync: True
hubblestack.nova.autoload: Truehubble.audit takes two optional arguments. The first is a comma-separated
list of paths. These paths can be files or directories. If a path is a
directory, all modules below that directory will be run. If it is a file, that
file will be run.
If hubble.audit is run without targeting any audit configs or directories,
it will instead run hubble.top with no arguments.
The second argument is a glob pattern, against which audit tags will be
matched. All audits have an accompanying tag. Nova modules are designed to take
this argument, compare it to each tag that module handles, and only run those
which match the argument (using fnmatch).
hubble.audit will return a list of audits which were successful, and a list
of audits which failed.
Here are some example calls:
# Run hubble.top with the default topfile (top.nova)
salt '*' hubble.audit
# Run all yaml configs and tags under salt://hubblestack_nova/foo/
# Will also run salt://hubblestack_nova/foo.yaml if it exists
salt '*' hubble.audit foo
# Run all yaml configs and tags under salt://hubblestack_nova/foo/ and
# salt://hubblestack_nova/bar, but only run audits with tags starting
# with "CIS"
salt '*' hubble.audit foo,bar tags='CIS*'Nova topfiles look very similar to saltstack topfiles, except the top-level
key is always nova, as nova doesn't have environments.
nova:
'*':
- cve_scan
- cis_gen
'web*':
- firewall
- cis-centos-7-l2-scored
- cis-centos-7-apache24-l1-scored
'G@os_family:debian':
- netstat
- cis-debian-7-l2-scored: 'CIS*'
- cis-debian-7-mysql57-l1-scored: 'CIS 2.1.2'Additionally, all nova topfile matches are compound matches, so you never need to define a match type like you do in saltstack topfiles.
Each list item is a string representing the dot-separated location of a yaml file which will be run with hubble.audit. You can also specify a tag glob to use as a filter for just that yaml file, using a colon after the yaml file (turning it into a dictionary). See the last two lines in the yaml above for examples.
Examples:
salt '*' hubble.top
salt '*' hubble.top foo/bar/top.nova
salt '*' hubble.top foo/bar.nova verbose=TrueIn some cases, your organization may want to skip certain audit checks for certain hosts. This is supported via compensating control configuration.
You can skip a check globally by adding a control: <reason> key to the check
itself. This key should be added at the same level as description and
trigger pieces of a check. In this case, the check will never run, and will
output under the Controlled results key.
Nova also supports separate control profiles, for more fine-grained control
using topfiles. You can use a separate yaml top-level key called control.
Generally, you'll put this top-level key inside of a separate yaml file and
only include it in the top-data for the hosts for which it is relevant.
For these separate control configs, the audits will always run, whether they
are controlled or not. However, controlled audits which fail will be converted
from Failure to Controlled in a post-processing operation.
The control config syntax is as follows:
control:
- CIS-2.1.4: This is the reason we control the check
- some_other_tag:
reason: This is the reason we control the check
- a_third_tag_with_no_reasonNote that providing a reason for the control is optional. Any of the three formats shown in the yaml list above will work.
Once you have your compensating control config, just target the yaml to the
hosts you want to control using your topfile. In this case, all the audits will
still run, but if any of the controlled checks fail, they will be removed from
Failure and added to Controlled, and will be treated as a Success for
the purposes of compliance percentage.
If you're interested in contributing to this project this section outlines the structure and requirements for Nova audit module development.
# -*- encoding: utf-8 -*-
'''
Loader and primary interface for nova modules
:maintainer: HubbleStack
:maturity: 20160214
:platform: Linux
:requires: SaltStack
'''
from __future__ import absolute_import
import loggingAll Nova plugins should include the above header, expanding the docstring to include full documentation
import fnmatch
import salt.utils
def __virtual__():
if salt.utils.is_windows():
return False, 'This audit module only runs on linux'
return True
def audit(data_list, tag, verbose=False):
__tags__ = []
for data in data_list:
# This is where you process the dictionaries passed in by hubble.py,
# searching for data pertaining to this audit module. Modules which
# require no data should use yaml which is empty except for a
# top-level key, and should only do work if the top-level key is
# found in the data
pass
ret = {'Success': [], 'Failure': []}
for tag in __tags__:
if fnmatch.fnmatch(tag, tags):
# We should run this tag
# <do audit stuff here>
ret['Success'].append(tag)
return retAll Nova plugins require a __virtual__() function to determine module
compatibility, and an audit() function to perform the actual audit
functionality
The audit() function must take three arguments, data_list, tag and
verbose. The data_list argument is a list of dictionaries passed in by
hubble.py. hubble.py gets this data from loading the specified yaml for
the audit run. Your audit module should only run if it finds its own data in
this list. The tag argument is a glob expression for which tags the audit
function should run. It is the job of the audit module to compare the tag
glob with all tags supported by this module and only run the audits which
match. The verbose argument defines whether additional information should
be returned for audits, such as description and remediation instructions.
The return value should be a dictionary, with two keys, "Success" and
"Failure". The values for these keys should be a list of tags as strings, or a
list of dictionaries containing tags and other information for the audit (in
the case of verbose).