Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
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)