def allcontainers(self): if len(self.cs.cons) == 0: error = "There are no containers on this system" raise ImageScannerClientError(error) else: con_list = [] for con in self.cs.cons: con_list.append(con['Id']) try: self._do_work(con_list) except Exception as error: raise ImageScannerClientError(str(error))
def _check_result(result): ''' Examines a json object looking for a key of 'Error' which indicates the previous call did not work. Raises an exception upon finding the key ''' result_json = json.loads(result.text) if 'Error' in result_json: raise ImageScannerClientError(result_json['Error']) if 'results' in result_json.keys() and 'Error' \ in result_json['results']: raise ImageScannerClientError(result_json['results']['Error'])
def allimages(self): if len(self.cs.imagelist) == 0: error = "There are no images on this system" raise ImageScannerClientError(error) if self.args.allimages: try: self._do_work(self.cs.allimagelist) except Exception as error: raise ImageScannerClientError(str(error)) else: try: self._do_work(self.cs.imagelist) except Exception as error: raise ImageScannerClientError(str(error))
def ValidateHost(self, host): ''' Validates if the defined docker host is running''' try: import docker except ImportError: error = "Can't import 'docker' package. Has docker been installed?" raise ImageScannerClientError(error) client = docker.Client(base_url=host, timeout=11) if not client.ping(): error = "Cannot connect to the Docker daemon. Is it running " \ "on this host?" raise ImageScannerClientError(error) return client
def onlyactive(self): ''' This function sorts of out only the active containers''' con_list = [] # Rid ourselves of 0 size containers for container in self.cs.active_containers: con_list.append(container['Id']) if len(con_list) == 0: error = "There are no active containers on this system" raise ImageScannerClientError(error) else: try: self._do_work(con_list) except Exception as error: raise ImageScannerClientError(str(error))
def _get_results(self, url, data=None, headers=None): '''Wrapper functoin for calling the request.session.get''' headers = self.request_headers if headers is None else headers try: if data is not None: results = self.get(url, data=data, headers=headers) else: results = self.get(url, headers=headers, timeout=9) except requests.exceptions.ConnectionError: raise ImageScannerClientError("Unable to connect to REST server " "at {0}".format(url)) except requests.exceptions.Timeout: raise ImageScannerClientError("Timeout reached with REST server " "at {0}".format(url)) return results
def _docker_ping(): d_conn = docker.Client() try: d_conn.ping() except Exception: raise ImageScannerClientError("The docker daemon does not appear" "to be running")
def _do_work(self, image_list): from oscap_docker_python.get_cve_input import getInputCVE self.scan_list = image_list cve_get = getInputCVE(self.image_tmp) if self.ac.fetch_cve_url != "": cve_get.url = self.ac.fetch_cve_url if self.ac.fetch_cve: cve_get.fetch_dist_data() threads = [] mnt_dir = tempfile.mkdtemp() for image in image_list: if image in self.cs.dead_cids: raise ImageScannerClientError("Scan not completed. Cannot " "scan the dead " "container {0}".format(image)) cids = self._get_cids_for_image(self.cs, image) t = threading.Thread(target=self.search_containers, name=image, args=( image, cids, self.output, mnt_dir, )) threads.append(t) logging.info("Number of containers to scan: {0}".format(len(threads))) if isinstance(threading.current_thread(), threading._MainThread): signal.signal(signal.SIGINT, self.signal_handler) self.threads_complete = 0 self.cur_scan_threads = 0 while len(threads) > 0: if self.cur_scan_threads < self.procs: new_thread = threads.pop() new_thread.start() self.cur_scan_threads += 1 while self.cur_scan_threads > 0: time.sleep(1) pass os.rmdir(mnt_dir) if self.failed_scan is not None: raise ImageScannerClientError(self.failed_scan) self.output.report_summary()
def _namesearch(self, input_name): """ Looks to see if the input name is the name of a image """ if ":" in input_name: image_name, tag = input_name.split(":") else: image_name = input_name tag = None name_search = self.ac.conn.images(name=image_name, all=True) # We found only one result, return it if len(name_search) == 1: return name_search[0]['Id'] else: # We found multiple images with the input name # If a tag is passed, then we can return the right one # If not, we assume if all the image_ids are same, we # can use that. ilist = [] for image in name_search: if input_name in image['RepoTags']: return image['Id'] else: ilist.append(image['Id']) if tag is not None: raise ImageScannerClientError( "Unable to find" "to an image named {0}".format(input_name)) # We didn't find it by name only. We check if the image_ids # are all the same if len(ilist) > 1: if all(ilist[0] == image for image in ilist) and (tag is None): return ilist[0] else: raise \ ImageScannerClientError("Found multiple images named" "{0} with different image Ids." "Try again with the image" "name and tag" .format(input_name)) return None
def scan_list(self, scan_list): if not isinstance(scan_list, list): raise ImageScannerClientError("Input to scan_list must be in" "the form of a list") ret = self.dbus_object.scan_list( scan_list, self.num_threads, dbus_interface=dbus_utils.DBUS_INTERFACE, timeout=self.db_timeout) return json.loads(ret)
def ValidateHost(self, host): ''' Validates if the defined docker host is running''' try: import docker except ImportError: error = "Can't import 'docker' package. Has docker been installed?" raise ImageScannerClientError(error) # Class docker.Client was renamed to docker.APIClient in # python-docker-py 2.0.0. try: client = docker.APIClient(base_url=host, timeout=11) except AttributeError: client = docker.Client(base_url=host, timeout=11) if not client.ping(): error = "Cannot connect to the Docker daemon. Is it running " \ "on this host?" raise ImageScannerClientError(error) return client
def getxml(self, url): ''' Given a URL string, returns the results of an openscap XML file as an Element Tree ''' try: results = self.get(url) except requests.exceptions.ConnectionError: raise ImageScannerClientError("Unable to connect to REST server " "at {0}".format(url)) return ET.ElementTree(ET.fromstring(results.content))
def get_docker_json(self, url): ''' Given a URL, return the state of the docker containers and images when the images-scanning occurred. Returns as JSON object. ''' try: results = self.get(url) except requests.exceptions.ConnectionError: raise ImageScannerClientError("Unable to connect to REST server " "at {0}".format(url)) return json.loads(results.text)
def ValidateHost(self, host): ''' Validates if the defined docker host is running''' try: client = docker.Client(base_url=host, timeout=11) if not client.ping(): raise(Exception) except Exception: error = "Cannot connect to the Docker daemon. Is it running on " \ "this host" client = None raise ImageScannerClientError(error) return client
def scan_list(self, scan_list): ''' Scans a list of containers/images by name or id and returns results in json ''' if not isinstance(scan_list, list): raise ImageScannerClientError("You must pass input in list form") url = urlparse.urljoin(self.host, self.api_path + "/scan") params = {'scan': scan_list, 'number': self.num_threads} results = self._get_results(url, data=json.dumps(params)) self._check_result(results) return json.loads(results.text)
def _docker_ping(): # Class docker.Client was renamed to docker.APIClient in # python-docker-py 2.0.0. try: d_conn = docker.APIClient() except AttributeError: d_conn = docker.Client() try: d_conn.ping() except Exception: raise ImageScannerClientError("The docker daemon does not appear" "to be running")
def report_results(self): if not os.path.exists(self.chroot_cve_file): from openscap_daemon.cve_scanner.scanner_error import ImageScannerClientError raise ImageScannerClientError("Unable to find {0}".format( self.chroot_cve_file)) return False cve_tree = ET.parse(bz2.BZ2File(self.chroot_cve_file)) self.cve_root = cve_tree.getroot() for line in self.result.splitlines(): split_line = line.split(':') # Not in love with how I did this # Should find a better marked to know if it is a line # a parsable line. if (len(split_line) == 5) and ('true' in split_line[4]): self._return_xml_values(line.split()[1][:-1]) sev_dict = {} sum_log = StringIO() sum_log.write("Image: {0} ({1})".format(self.image_name, self.os_release)) cons = self.get_cons(self.ac.fcons, self.image_name) sum_log.write("\nContainers based on this image ({0}): {1}\n".format( len(cons), ", ".join(cons))) for sev in ['Critical', 'Important', 'Moderate', 'Low']: sev_counter = 0 for cve in self.list_of_CVEs: if cve.severity == sev: sev_counter += 1 sum_log.write("\n") fields = list(self.CVEs._fields) fields.remove('title') sum_log.write("{0}{1}: {2}\n".format( " " * 5, "Title", getattr(cve, "title"))) for field in fields: sum_log.write("{0}{1}: {2}\n".format( " " * 10, field.title(), getattr(cve, field))) sev_dict[sev] = sev_counter self.output.list_of_outputs.append( self.output.output(iid=self.image_name, cid=self.con_uuids, os=self.os_release, sevs=sev_dict, log=sum_log.getvalue(), msg=None)) sum_log.close()
def ping(self): ''' Throws an exception if it cannot access the REST server or the docker host ''' url = urlparse.urljoin(self.host, self.api_path + "/ping") results = self._get_results(url) if 'results' not in json.loads(results.text): tmp_obj = json.loads(results.text) if hasattr(tmp_obj, 'error'): error = getattr(tmp_obj, 'error') else: error = tmp_obj['Error'] error = error.replace('on the host ', 'on the host {0} '.format(self.host)) raise ImageScannerClientError(error)
def _check_input(self, image_list): ''' Takes a list of image ids, image-names, container ids, or container-names and returns a list of images ids and container ids ''' work_list = [] # verify try: for image in image_list: iid = self.get_iid(image) work_list.append(iid) except ImageScannerClientError: error = "Unable to associate {0} with any image " \ "or container".format(image) raise ImageScannerClientError(error) return work_list
def is_id_an_image(self, docker_id, docker_obj): ''' helper function that uses the docker_state_file to validate if the given item_id is a container or image id ''' if self.containers is None or self.images is None: self.containers = docker_obj['host_containers'] self.images = docker_obj['host_images'] if docker_id in self.images: return True elif docker_id in self.containers: return False else: # Item was not found in the docker state file error_msg = 'The provided openscap xml result file was ' \ 'not generated from the same run as the ' \ 'docker state file ' raise ImageScannerClientError(error_msg)
def get_profile_info(self, profile): ''' Looks for host and port based on the profile provided ''' config = ConfigParser.RawConfigParser() config.read(self.config_file) try: port = config.get(profile, 'port') host = config.get(profile, 'host') cert = None if not config.has_option(profile, 'cert') else \ config.get(profile, 'cert') number = 2 if not config.has_option(profile, 'threads') else \ config.get(profile, 'threads') except ConfigParser.NoSectionError: raise ImageScannerClientError("The profile {0} cannot be found " "in {1}".format( profile, self.config_file)) except ConfigParser.NoOptionError as no_option: print("No option {0} found in profile "\ "{1} in {2}".format(no_option.option, profile, self.config_file)) return host, port, number, cert
def get_iid(self, input_name): ''' Find the image id based on a input_name which can be an image id, image name, or an image name:tag name. ''' # Check if the input name is a container cid = self.get_cid(input_name) if cid is not None: return cid # Check if the input_name was an image name or name:tag image_id = self._namesearch(input_name) if image_id is not None: return image_id # Maybe input name is an image id (or portion) for image in self.ac.allimages: if image['Id'].startswith(input_name): return image['Id'] raise ImageScannerClientError( "Unable to associate {0} with any image".format(input_name))
def _check_profile_is_valid(all_profile_names, profile_list): ''' Checks a list of profiles to make sure they are valid ''' for profile in profile_list: if profile not in all_profile_names: raise ImageScannerClientError( "Profile {0} is invalid".format(profile))
def scan_multiple_hosts(self, profile_list, allimages=False, images=False, allcontainers=False, onlyactive=False, remote_threads=4, threads=0): ''' Scan multiple hosts and returns an uber-docker object which is basically an object with one or more docker state objects in it. ''' if (threads > 0): self.threads = threads if (threads < 2 or threads > 4): raise ImageScannerClientError("Thread count must be between 2 " "and 4") scan_args = self.args_tuple(allimages=allimages, images=images, allcontainers=allcontainers, onlyactive=onlyactive) # Check to make sure a scan type was selected if not scan_args.allimages and not scan_args.images and not \ scan_args.allcontainers and not scan_args.onlyactive: raise ImageScannerClientError("You must select \ a scan type") # Check to make sure only one scan type was selected if len([ x for x in [ scan_args.allimages, scan_args.images, scan_args.allcontainers, scan_args.onlyactive ] if x is True ]) > 1: raise ImageScannerClientError("You may only select one \ type of scan") # Check profile names are valid all_profile_names = self.get_all_profile_names() self._check_profile_is_valid(all_profile_names, profile_list) # Obtain list of profiles profiles = self.return_profiles(profile_list) self.num_total = len(profiles) # FIXME # Make this a variable based on desired number pool = ThreadPool(remote_threads) pool.map(self.thread_profile_wrapper, [(x, scan_args.onlyactive, scan_args.allcontainers, scan_args.allimages, scan_args.images) for x in profiles]) with open(self.uber_file_path, 'w') as state_file: json.dump(self.uber_docker, state_file) return self.uber_docker
def list_of_images(self, image_list): try: self._do_work(image_list) except Exception as error: raise ImageScannerClientError(str(error))