def parse_args(args): prog = ArgumentParser( config_file_parser_class=YAMLConfigFileParser, formatter_class=argparse.RawDescriptionHelpFormatter, default_config_files=['~/.config/seal', '~/.seal'], description=textwrap.dedent("""\ PowerfulSeal """), ) # General settings prog.add_argument( '-c', '--config', is_config_file=True, env_var="CONFIG", help='Config file path', ) prog.add_argument('-v', '--verbose', action='count', help='Verbose logging.') # Policy # If --validate-policy-file is set, the other arguments are not used policy_options = prog.add_mutually_exclusive_group(required=True) policy_options.add_argument( '--validate-policy-file', help='reads the policy file, validates the schema, returns') policy_options.add_argument( '--run-policy-file', default=os.environ.get("POLICY_FILE"), help='location of the policy file to read', ) policy_options.add_argument( '--interactive', help='will start the seal in interactive mode', action='store_true', ) policy_options.add_argument( '--label', help='starts the seal in label mode', action='store_true', ) policy_options.add_argument('--demo', help='starts the demo mode', action='store_true') is_validate_policy_file_set = '--validate-policy-file' in args # Demo mode demo_options = prog.add_argument_group() demo_options.add_argument( '--heapster-path', help='Base path of Heapster without trailing slash') demo_options.add_argument('--aggressiveness', help='Aggressiveness of demo mode (default: 3)', default=3, type=int) # Arguments for both label and demo mode prog.add_argument( '--namespace', default='default', help='Namespace to use for label and demo mode, defaults to the default ' 'namespace (set to blank for all namespaces)') prog.add_argument('--min-seconds-between-runs', help='Minimum number of seconds between runs', default=0, type=int) prog.add_argument('--max-seconds-between-runs', help='Maximum number of seconds between runs', default=300, type=int) # Web prog.add_argument('--server', help='Start PowerfulSeal in web server mode', action='store_true') prog.add_argument('--server-host', help='Specify host for the PowerfulSeal web server') prog.add_argument('--server-port', help='Specify port for the PowerfulSeal web server') # Inventory inventory_options = prog.add_mutually_exclusive_group( required=not is_validate_policy_file_set) inventory_options.add_argument( '-i', '--inventory-file', default=os.environ.get("INVENTORY_FILE"), help='the inventory file of group of hosts to test') inventory_options.add_argument( '--inventory-kubernetes', default=os.environ.get("INVENTORY_KUBERNETES"), help='will read all cluster nodes as inventory', action='store_true', ) # SSH args_ssh = prog.add_argument_group('SSH settings') args_ssh.add_argument( '--remote-user', default=os.environ.get("PS_REMOTE_USER", "cloud-user"), help="the of the user for the ssh connections", ) args_ssh.add_argument( '--ssh-allow-missing-host-keys', default=False, action='store_true', help='Allow connection to hosts not present in known_hosts', ) args_ssh.add_argument( '--ssh-path-to-private-key', default=os.environ.get("PS_PRIVATE_KEY"), help='Path to ssh private key', ) # Cloud Driver cloud_options = prog.add_mutually_exclusive_group( required=not is_validate_policy_file_set) cloud_options.add_argument( '--open-stack-cloud', default=os.environ.get("OPENSTACK_CLOUD"), action='store_true', help="use OpenStack cloud provider", ) cloud_options.add_argument( '--aws-cloud', default=os.environ.get("AWS_CLOUD"), action='store_true', help="use AWS cloud provider", ) cloud_options.add_argument( '--no-cloud', default=os.environ.get("NO_CLOUD"), action='store_true', help="don't use cloud provider", ) prog.add_argument( '--open-stack-cloud-name', default=os.environ.get("OPENSTACK_CLOUD_NAME"), help= "the name of the open stack cloud from your config file to use (if using config file)", ) # Metric Collector metric_options = prog.add_mutually_exclusive_group(required=False) metric_options.add_argument('--stdout-collector', default=os.environ.get("STDOUT_COLLECTOR"), action='store_true', help="print metrics collected to stdout") metric_options.add_argument( '--prometheus-collector', default=os.environ.get("PROMETHEUS_COLLECTOR"), action='store_true', help="store metrics in Prometheus and expose metrics over a HTTP server" ) def check_valid_port(value): parsed = int(value) min_port = 0 max_port = 65535 if parsed < min_port or parsed > max_port: raise argparse.ArgumentTypeError("%s is an invalid port number" % value) return parsed args_prometheus = prog.add_argument_group('Prometheus settings') args_prometheus.add_argument( '--prometheus-host', default='127.0.0.1', help= 'Host to expose Prometheus metrics via the HTTP server when using the --prometheus-collector flag' ) args_prometheus.add_argument( '--prometheus-port', default=8000, help= 'Port to expose Prometheus metrics via the HTTP server when using the --prometheus-collector flag', type=check_valid_port) # Kubernetes args_kubernetes = prog.add_argument_group('Kubernetes settings') args_kubernetes.add_argument( '--kube-config', default=None, help='Location of kube-config file', ) return prog.parse_args(args=args)
def main(argv): """ The main function to invoke the powerfulseal cli """ # Describe our configuration. prog = ArgumentParser( config_file_parser_class=YAMLConfigFileParser, formatter_class=argparse.RawDescriptionHelpFormatter, default_config_files=['~/.config/seal', '~/.seal'], description=textwrap.dedent("""\ PowerfulSeal """), ) # general settings prog.add_argument( '-c', '--config', is_config_file=True, env_var="CONFIG", help='Config file path', ) prog.add_argument('-v', '--verbose', action='count', help='Verbose logging.' ) # inventory related config inventory_options = prog.add_mutually_exclusive_group(required=True) inventory_options.add_argument('-i', '--inventory-file', default=os.environ.get("INVENTORY_FILE"), help='the inventory file of group of hosts to test' ) inventory_options.add_argument('--inventory-kubernetes', default=os.environ.get("INVENTORY_KUBERNETES"), help='will read all cluster nodes as inventory', action='store_true', ) # ssh related options args_ssh = prog.add_argument_group('SSH settings') args_ssh.add_argument( '--remote-user', default=os.environ.get("PS_REMOTE_USER", "cloud-user"), help="the of the user for the ssh connections", ) args_ssh.add_argument( '--ssh-allow-missing-host-keys', default=False, action='store_true', help='Allow connection to hosts not present in known_hosts', ) args_ssh.add_argument( '--ssh-path-to-private-key', default=os.environ.get("PS_PRIVATE_KEY"), help='Path to ssh private key', ) # cloud driver related config cloud_options = prog.add_mutually_exclusive_group(required=True) cloud_options.add_argument('--open-stack-cloud', default=os.environ.get("OPENSTACK_CLOUD"), action='store_true', help="use OpenStack cloud provider", ) cloud_options.add_argument('--aws-cloud', default=os.environ.get("AWS_CLOUD"), action='store_true', help="use AWS cloud provider", ) cloud_options.add_argument('--no-cloud', default=os.environ.get("NO_CLOUD"), action='store_true', help="don't use cloud provider", ) prog.add_argument('--open-stack-cloud-name', default=os.environ.get("OPENSTACK_CLOUD_NAME"), help="the name of the open stack cloud from your config file to use (if using config file)", ) # KUBERNETES CONFIG args_kubernetes = prog.add_argument_group('Kubernetes settings') args_kubernetes.add_argument( '--kube-config', default=None, help='Location of kube-config file', ) # policy-related settings policy_options = prog.add_mutually_exclusive_group(required=True) policy_options.add_argument('--validate-policy-file', help='reads the policy file, validates the schema, returns' ) policy_options.add_argument('--run-policy-file', default=os.environ.get("POLICY_FILE"), help='location of the policy file to read', ) policy_options.add_argument('--interactive', help='will start the seal in interactive mode', action='store_true', ) args = prog.parse_args(args=argv) # Configure logging if not args.verbose: log_level = logging.ERROR elif args.verbose == 1: log_level = logging.WARNING elif args.verbose == 2: log_level = logging.INFO else: log_level = logging.DEBUG logging.basicConfig( stream=sys.stdout, level=log_level ) logger = logging.getLogger(__name__) logger.setLevel(log_level) # build cloud provider driver logger.debug("Building the driver") if args.open_stack_cloud: logger.info("Building OpenStack driver") driver = OpenStackDriver( cloud=args.open_stack_cloud_name, ) elif args.aws_cloud: logger.info("Building AWS driver") driver = AWSDriver() else: logger.info("No driver - some functionality disabled") driver = NoCloudDriver() # build a k8s client kube_config = args.kube_config logger.debug("Creating kubernetes client with config %d", kube_config) k8s_client = K8sClient(kube_config=kube_config) k8s_inventory = K8sInventory(k8s_client=k8s_client) # read the local inventory logger.debug("Fetching the inventory") if args.inventory_file: groups_to_restrict_to = read_inventory_file_to_dict( args.inventory_file ) else: logger.info("Attempting to read the inventory from kubernetes") groups_to_restrict_to = k8s_client.get_nodes_groups() logger.debug("Restricting inventory to %s" % groups_to_restrict_to) inventory = NodeInventory( driver=driver, restrict_to_groups=groups_to_restrict_to, ) inventory.sync() # create an executor executor = RemoteExecutor( user=args.remote_user, ssh_allow_missing_host_keys=args.ssh_allow_missing_host_keys, ssh_path_to_private_key=args.ssh_path_to_private_key, ) if args.interactive: # create a command parser cmd = PSCmd( inventory=inventory, driver=driver, executor=executor, k8s_inventory=k8s_inventory, ) while True: try: cmd.cmdloop() except KeyboardInterrupt: print() print("Ctrl-c again to quit") try: input() except KeyboardInterrupt: sys.exit(0) elif args.validate_policy_file: PolicyRunner.validate_file(args.validate_policy_file) print("All good, captain") elif args.run_policy_file: policy = PolicyRunner.validate_file(args.run_policy_file) PolicyRunner.run(policy, inventory, k8s_inventory, driver, executor)
def add_parser_args(parser: ArgumentParser) -> None: parser.add('-v', '--version', help='version', action='version', version=version) parser.add( '-d', '--directory', action='append', help='IaC root directory (can not be used together with --file).') parser.add('--add-check', action='store_true', help="Generate a new check via CLI prompt") parser.add('-f', '--file', action='append', help='IaC file(can not be used together with --directory)') parser.add( '--skip-path', action='append', help= 'Path (file or directory) to skip, using regular expression logic, relative to current ' 'working directory. Word boundaries are not implicit; i.e., specifying "dir1" will skip any ' 'directory or subdirectory named "dir1". Ignored with -f. Can be specified multiple times.' ) parser.add( '--external-checks-dir', action='append', help='Directory for custom checks to be loaded. Can be repeated') parser.add( '--external-checks-git', action='append', help= 'Github url of external checks to be added. \n you can specify a subdirectory after a ' 'double-slash //. \n cannot be used together with --external-checks-dir' ) parser.add('-l', '--list', help='List checks', action='store_true') parser.add( '-o', '--output', action='append', choices=OUTPUT_CHOICES, default=None, help= 'Report output format. Add multiple outputs by using the flag multiple times (-o sarif -o cli)' ) parser.add( '--output-file-path', default=None, help= 'Name for output file. The first selected output via output flag will be saved to the file (default output is cli)' ) parser.add( '--output-bc-ids', action='store_true', help= 'Print Bridgecrew platform IDs (BC...) instead of Checkov IDs (CKV...), if the check exists in the platform' ) parser.add( '--no-guide', action='store_true', default=False, help= 'Do not fetch Bridgecrew platform IDs and guidelines for the checkov output report. Note: this ' 'prevents Bridgecrew platform check IDs from being used anywhere in the CLI.' ) parser.add('--quiet', action='store_true', default=False, help='in case of CLI output, display only failed checks') parser.add('--compact', action='store_true', default=False, help='in case of CLI output, do not display code blocks') parser.add( '--framework', help= 'filter scan to run only on specific infrastructure code frameworks', choices=checkov_runners + ["all"], default=['all'], nargs="+") parser.add( '--skip-framework', help='filter scan to skip specific infrastructure code frameworks. \n' 'will be included automatically for some frameworks if system dependencies ' 'are missing.', choices=checkov_runners, default=None, nargs="+") parser.add( '-c', '--check', help= 'filter scan to run only on a specific check identifier(allowlist), You can ' 'specify multiple checks separated by comma delimiter', action='append', default=None, env_var='CKV_CHECK') parser.add( '--skip-check', help= 'filter scan to run on all check but a specific check identifier(denylist), You can ' 'specify multiple checks separated by comma delimiter', action='append', default=None, env_var='CKV_SKIP_CHECK') parser.add( '--run-all-external-checks', action='store_true', help= 'Run all external checks (loaded via --external-checks options) even if the checks are not present ' 'in the --check list. This allows you to always ensure that new checks present in the external ' 'source are used. If an external check is included in --skip-check, it will still be skipped.' ) parser.add('--bc-api-key', env_var='BC_API_KEY', sanitize=True, help='Bridgecrew API key') parser.add( '--prisma-api-url', env_var='PRISMA_API_URL', default=None, help= 'The Prisma Cloud API URL (see: https://prisma.pan.dev/api/cloud/api-urls). ' 'Requires --bc-api-key to be a Prisma Cloud Access Key in the following format: <access_key_id>::<secret_key>' ) parser.add( '--docker-image', help= 'Scan docker images by name or ID. Only works with --bc-api-key flag') parser.add('--dockerfile-path', help='Path to the Dockerfile of the scanned docker image') parser.add( '--repo-id', help= 'Identity string of the repository, with form <repo_owner>/<repo_name>' ) parser.add( '-b', '--branch', help= "Selected branch of the persisted repository. Only has effect when using the --bc-api-key flag", default='master') parser.add( '--skip-fixes', help= 'Do not download fixed resource templates from Bridgecrew. Only has effect when using the ' '--bc-api-key flag', action='store_true') parser.add( '--skip-suppressions', help= 'Do not download preconfigured suppressions from the Bridgecrew platform. Code comment ' 'suppressions will still be honored. ' 'Only has effect when using the --bc-api-key flag', action='store_true') parser.add( '--skip-policy-download', help= 'Do not download custom policies configured in the Bridgecrew platform. ' 'Only has effect when using the --bc-api-key flag', action='store_true') parser.add( '--download-external-modules', help= "download external terraform modules from public git repositories and terraform registry", default=os.environ.get('DOWNLOAD_EXTERNAL_MODULES', False), env_var='DOWNLOAD_EXTERNAL_MODULES') parser.add( '--var-file', action='append', help='Variable files to load in addition to the default files (see ' 'https://www.terraform.io/docs/language/values/variables.html#variable-definitions-tfvars-files).' 'Currently only supported for source Terraform (.tf file), and Helm chart scans.' 'Requires using --directory, not --file.') parser.add('--external-modules-download-path', help="set the path for the download external terraform modules", default=DEFAULT_EXTERNAL_MODULES_DIR, env_var='EXTERNAL_MODULES_DIR') parser.add('--evaluate-variables', help="evaluate the values of variables and locals", default=True) parser.add('-ca', '--ca-certificate', help='Custom CA certificate (bundle) file', default=None, env_var='BC_CA_BUNDLE') parser.add( '--repo-root-for-plan-enrichment', help= 'Directory containing the hcl code used to generate a given plan file. Use with -f.', dest="repo_root_for_plan_enrichment", action='append') parser.add('--config-file', help='path to the Checkov configuration YAML file', is_config_file=True, default=None) parser.add( '--create-config', help= 'takes the current command line args and writes them out to a config file at ' 'the given path', is_write_out_config_file_arg=True, default=None) parser.add( '--show-config', help='prints all args and config settings and where they came from ' '(eg. commandline, config file, environment variable or default)', action='store_true', default=None) parser.add( '--create-baseline', help= 'Alongside outputting the findings, save all results to .checkov.baseline file' ' so future runs will not re-flag the same noise. Works only with `--directory` flag', action='store_true', default=False) parser.add( '--baseline', help= ("Use a .checkov.baseline file to compare current results with a known baseline. " "Report will include only failed checks that are new with respect to the provided baseline" ), default=None, ) parser.add( '--min-cve-severity', help= 'Set minimum severity that will cause returning non-zero exit code', choices=SEVERITY_RANKING.keys(), default='none') parser.add( '--skip-cve-package', help= 'filter scan to run on all packages but a specific package identifier (denylist), You can ' 'specify this argument multiple times to skip multiple packages', action='append', default=None) # Add mutually exclusive groups of arguments exit_code_group = parser.add_mutually_exclusive_group() exit_code_group.add('-s', '--soft-fail', help='Runs checks but suppresses error code', action='store_true') exit_code_group.add( '--soft-fail-on', help='Exits with a 0 exit code for specified checks. You can specify ' 'multiple checks separated by comma delimiter', action='append', default=None) exit_code_group.add( '--hard-fail-on', help= 'Exits with a non-zero exit code for specified checks. You can specify ' 'multiple checks separated by comma delimiter', action='append', default=None)