#!/usr/bin/python # # Copyright (c) 2016 Red Hat # Luke Hinds (lhinds@redhat.com) # This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 # which accompanies this distribution, and is available at # # http://www.apache.org/licenses/LICENSE-2.0 # # 0.1: This script installs OpenSCAP on the remote host, and scans the # nominated node. Post scan a report is downloaded and if '--clean' is passed # all trace of the scan is removed from the remote system. import argparse import connect import datetime import os import sys from ConfigParser import SafeConfigParser from keystoneclient.auth.identity import v2 from keystoneclient import session from novaclient import client __version__ = 0.1 __author__ = 'Luke Hinds (lhinds@redhat.com)' __url__ = 'https://wiki.opnfv.org/display/functest/Functest+Security' # Global vars INSTALLER_IP = os.getenv('INSTALLER_IP') oscapbin = 'sudo /bin/oscap' functest_dir = '/home/opnfv/repos/functest/testcases/security_scan/' # Apex Spefic var needed to query Undercloud if os.getenv('OS_AUTH_URL') is None: connect.logger.error(" Enviroment variable OS_AUTH_URL is not set") sys.exit(0) else: OS_AUTH_URL = os.getenv('OS_AUTH_URL') # args parser = argparse.ArgumentParser(description='OPNFV OpenSCAP Scanner') parser.add_argument('--config', action='store', dest='cfgfile', help='Config file', required=True) args = parser.parse_args() # Config Parser cfgparse = SafeConfigParser() cfgparse.read(args.cfgfile) # Obtain Undercloud key remotekey = cfgparse.get('undercloud', 'remotekey') localkey = cfgparse.get('undercloud', 'localkey') setup = connect.SetUp(remotekey, localkey) setup.getockey() # Configure Nova Credentials com = 'sudo /usr/bin/hiera admin_password' setup = connect.SetUp(com) keypass = setup.keystonepass() auth = v2.Password(auth_url=OS_AUTH_URL, username='admin', password=str(keypass).rstrip(), tenant_name='admin') sess = session.Session(auth=auth) nova = client.Client(2, session=sess) def run_tests(host, nodetype): """ Main tool runtime function """ user = cfgparse.get(nodetype, 'user') port = cfgparse.get(nodetype, 'port') connect.logger.info("Host: {0} Selected Profile: {1}".format(host, nodetype)) connect.logger.debug("Checking internet for package installation...") if internet_check(host, nodetype): connect.logger.debug("Internet Connection OK.") connect.logger.debug("Creating temp file structure..") createfiles(host, port, user, localkey) connect.logger.debug("Installing OpenSCAP...") install_pkg(host, port, user, localkey) connect.logger.debug("Running scan...") run_scanner(host, port, user, localkey, nodetype) clean = cfgparse.get(nodetype, 'clean') connect.logger.debug("Post installation tasks....") post_tasks(host, port, user, localkey, nodetype) if clean: connect.logger.debug("Cleaning down environment....") connect.logger.debug("Removing OpenSCAP....") removepkg(host, port, user, localkey, nodetype) connect.logger.debug("Deleting tmp file and reports (remote)...") cleandir(host, port, user, localkey, nodetype) else: connect.logger.error("Internet timeout. Moving on to next node..") pass def nova_iterate(): """ Iterates over the Nova API to gather a list of node IP's""" for server in nova.servers.list(): if server.status == 'ACTIVE' and 'compute' in server.name: networks = server.networks nodetype = 'compute' for host in networks['ctlplane']: run_tests(host, nodetype) # Find controller nodes, active with network on ctlplane elif server.status == 'ACTIVE' and 'controller' in server.name: networks = server.networks nodetype = 'controller' for host in networks['ctlplane']: run_tests(host, nodetype) def internet_check(host, nodetype): """ Performs connectivity test using scripts/internet_check.py """ import connect user = cfgparse.get(nodetype, 'user') port = cfgparse.get(nodetype, 'port') localpath = functest_dir + 'scripts/internet_check.py' remotepath = '/tmp/internet_check.py' com = 'python /tmp/internet_check.py' testconnect = connect.ConnectionManager(host, port, user, localkey, localpath, remotepath, com) connectionresult = testconnect.remotescript() if connectionresult.rstrip() == 'True': return True else: return False def createfiles(host, port, user, localkey): """ Creates required tempfiles needed for OpenSCAP to run. Executes script file: /scripts/createfiles.py """ import connect global tmpdir localpath = functest_dir + 'scripts/createfiles.py' remotepath = '/tmp/createfiles.py' com = 'python /tmp/createfiles.py' connect = connect.ConnectionManager(host, port, user, localkey, localpath, remotepath, com) tmpdir = connect.remotescript() def install_pkg(host, port, user, localkey): """ Installs OpenSCAP binarie and main release scap content""" import connect com = 'sudo yum -y install openscap-scanner scap-security-guide' connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() def run_scanner(host, port, user, localkey, nodetype): """ Peforms the actual OpenSCAP scan operation""" import connect scantype = cfgparse.get(nodetype, 'scantype') profile = cfgparse.get(nodetype, 'profile') results = cfgparse.get(nodetype, 'results') report = cfgparse.get(nodetype, 'report') secpolicy = cfgparse.get(nodetype, 'secpolicy') # Here is where we contruct the actual scan command if scantype == 'xccdf': cpe = cfgparse.get(nodetype, 'cpe') com = '{0} xccdf eval --profile {1} --results {2}/{3}' \ ' --report {2}/{4} --cpe {5} {6}'.format(oscapbin, profile, tmpdir.rstrip(), results, report, cpe, secpolicy) connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() elif scantype == 'oval': com = '{0} oval eval --results {1}/{2} ' '--report {1}/{3} {4}'.format(oscapbin, tmpdir.rstrip(), results, report, secpolicy) connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() else: com = '{0} oval-collect '.format(oscapbin) connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() def post_tasks(host, port, user, localkey, nodetype): """ Create download folder for functest dashboard and download reports """ import connect reports_dir = cfgparse.get(nodetype, 'reports_dir') dl_folder = os.path.join(reports_dir, host + "_" + datetime.datetime. now().strftime('%Y-%m-%d_%H-%M-%S')) os.makedirs(dl_folder, 0755) report = cfgparse.get(nodetype, 'report') results = cfgparse.get(nodetype, 'results') reportfile = '{0}/{1}'.format(tmpdir.rstrip(), report) connect = connect.ConnectionManager(host, port, user, localkey, dl_folder, reportfile, report, results) connect.download_reports() def removepkg(host, port, user, localkey, nodetype): """ Removes all packages (if Clean = True is used in ini config) """ import connect com = 'sudo yum -y remove openscap-scanner scap-security-guide' connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() def cleandir(host, port, user, localkey, nodetype): """ Removes all scan files (if Clean = True is used in ini config) """ import connect com = 'sudo rm -r {0}'.format(tmpdir.rstrip()) connect = connect.ConnectionManager(host, port, user, localkey, com) connect.remotecmd() if __name__ == '__main__': """ Main entry_point """ nova_iterate()