コード例 #1
0
    def list_scenario_instance(self, scenario_name_or_path):
        scenario_name = normalize_scenario_name(scenario_name_or_path)
        scenario_instance_dir_path = find_scenario_instance_dir(
            self.base_dir, scenario_name)

        if scenario_instance_dir_path is None:
            print(
                f'[cloudgoat] Error: No scenario instance for "{scenario_name}" found.'
                f" Try: cloudgoat.py list deployed")
            return

        terraform = Terraform(
            working_dir=os.path.join(scenario_instance_dir_path, "terraform"))

        show_retcode, show_stdout, show_stderr = terraform.show(
            capture_output=False, no_color=IsNotFlagged)
        if show_retcode != 0:
            display_terraform_step_error("terraform show", show_retcode,
                                         show_stdout, show_stderr)
            return
        else:
            print(
                f"\n[cloudgoat] terraform show completed with no error code.")

        return
コード例 #2
0
    def display_cloudgoat_help(self, command):
        if not command or len(command) == 1:
            return print(help_text.CLOUDGOAT)

        # Makes "help foo" equivalent to "foo help".
        command.remove("help")

        if command[0] == "config":
            if len(command) > 1 and command[1] == "argcomplete":
                return print(help_text.CONFIG_ARGCOMPLETE)
            else:
                return print(help_text.CONFIG)
        elif command[0] == "create":
            return print(help_text.CREATE)
        elif command[0] == "destroy":
            return print(help_text.DESTROY)
        elif command[0] == "list":
            return print(help_text.LIST)
        elif command[0] == "help":
            if all([word == "help" for word in command]):
                joined_help_texts = " ".join(
                    ["help text for" for word in command])
                return print(f"Displays {joined_help_texts} CloudGoat.")
        else:
            scenario_name = normalize_scenario_name(command[0])
            scenario_dir_path = find_scenario_dir(self.scenarios_dir,
                                                  scenario_name)
            if scenario_dir_path:
                scenario_help_text = load_data_from_yaml_file(
                    os.path.join(scenario_dir_path, "manifest.yml"),
                    "help").strip()
                return print(
                    f"[cloudgoat scenario: {scenario_name}]\n{scenario_help_text}"
                )

        return print(
            f'Unrecognized command or scenario name. Try "cloudgoat.py help" or'
            f' "cloudgoat.py list all"')
コード例 #3
0
    def destroy_scenario(self,
                         scenario_name_or_path,
                         profile,
                         confirmed=False):
        # Information gathering.
        scenario_name = normalize_scenario_name(scenario_name_or_path)
        scenario_instance_dir_path = find_scenario_instance_dir(
            self.base_dir, scenario_name)

        if scenario_instance_dir_path is None:
            print(
                f'[cloudgoat] Error: No scenario instance for "{scenario_name}" found.'
                f" Try: cloudgoat.py list deployed")
            return

        instance_name = os.path.basename(scenario_instance_dir_path)

        # Confirmation.
        if not confirmed:
            delete_permission = input(
                f'Destroy "{instance_name}"? [y/n]: ').strip()
            if not delete_permission or not delete_permission[0].lower(
            ) == "y":
                print(f"\nCancelled destruction of {instance_name}.\n")
                return

        # Terraform execution.
        terraform_directory = os.path.join(scenario_instance_dir_path,
                                           "terraform")

        if os.path.exists(
                os.path.join(terraform_directory, "terraform.tfstate")):
            terraform = Terraform(working_dir=terraform_directory)

            cgid = extract_cgid_from_dir_name(
                os.path.basename(scenario_instance_dir_path))

            destroy_retcode, destroy_stdout, destroy_stderr = terraform.destroy(
                capture_output=False,
                var={
                    "cgid": cgid,
                    "cg_whitelist": list(),
                    "profile": profile,
                    "region": self.aws_region,
                },
                no_color=IsNotFlagged,
            )
            if destroy_retcode != 0:
                display_terraform_step_error("terraform destroy",
                                             destroy_retcode, destroy_stdout,
                                             destroy_stderr)
                return
            else:
                print(
                    "\n[cloudgoat] terraform destroy completed with no error code."
                )
        else:
            print(
                f"\nNo terraform.tfstate file was found in the scenario instance's"
                f' terraform directory, so "terraform destroy" will not be run.'
            )

        # Scenario instance directory trashing.
        trash_dir = create_dir_if_nonexistent(self.base_dir, "trash")

        trashed_instance_path = os.path.join(
            trash_dir, os.path.basename(scenario_instance_dir_path))

        shutil.move(scenario_instance_dir_path, trashed_instance_path)

        print(
            f"\nSuccessfully destroyed {instance_name}."
            f"\nScenario instance files have been moved to {trashed_instance_path}"
        )

        return
コード例 #4
0
    def create_scenario(self, scenario_name_or_path, profile):
        scenario_name = normalize_scenario_name(scenario_name_or_path)
        scenario_dir = os.path.join(self.scenarios_dir, scenario_name)

        if not scenario_dir or not scenario_name or not os.path.exists(
                scenario_dir):
            if not scenario_name:
                return print(
                    f"No recognized scenario name was entered. Did you mean one of"
                    f" these?\n    " + f"\n    ".join(self.scenario_names))
            else:
                return print(
                    f"No scenario named {scenario_name} exists in the scenarios"
                    f" directory. Did you mean one of these?"
                    f"\n    " + f"\n    ".join(self.scenario_names))

        if not os.path.exists(self.whitelist_path):
            cg_whitelist = self.configure_or_check_whitelist(auto=True)
        else:
            cg_whitelist = self.configure_or_check_whitelist()

        if not cg_whitelist:
            print(
                f"A valid whitelist.txt file must exist in the {self.base_dir}"
                f' directory before "create" may be used.')
            return

        # Create a scenario-instance folder in the project root directory.
        # This command should fail with an explanatory error message if a
        # scenario-instance of the same root name (i.e. without the CGID) already
        # exists.
        extant_dir = find_scenario_instance_dir(self.base_dir, scenario_name)
        if extant_dir is not None:
            destroy_and_recreate = input(
                f"You already have an instance of {scenario_name} deployed."
                f" Do you want to destroy and recreate it (y) or cancel (n)? [y/n]: "
            )

            if destroy_and_recreate.strip().lower() == "y":
                self.destroy_scenario(scenario_name, profile, confirmed=True)
            else:
                instance_name = os.path.basename(extant_dir)
                print(
                    f"\nCancelled destruction and recreation of {instance_name}.\n"
                )
                return

        cgid = generate_cgid()
        scenario_instance_dir_path = os.path.join(self.base_dir,
                                                  f"{scenario_name}_{cgid}")

        # Copy all the terraform files from the "/scenarios/scenario-name" folder
        # to the scenario-instance folder.
        source_dir_contents = os.path.join(scenario_dir, ".")
        shutil.copytree(source_dir_contents, scenario_instance_dir_path)

        if os.path.exists(os.path.join(scenario_instance_dir_path,
                                       "start.sh")):
            print(f"\nNow running {scenario_name}'s start.sh...")
            start_script_process = subprocess.Popen(
                ["sh", "start.sh"], cwd=scenario_instance_dir_path)
            start_script_process.wait()
        else:
            pass

        terraform = Terraform(
            working_dir=os.path.join(scenario_instance_dir_path, "terraform"))

        init_retcode, init_stdout, init_stderr = terraform.init(
            capture_output=False, no_color=IsNotFlagged)
        if init_retcode != 0:
            display_terraform_step_error("terraform init", init_retcode,
                                         init_stdout, init_stderr)
            return
        else:
            print(
                f"\n[cloudgoat] terraform init completed with no error code.")

        plan_retcode, plan_stdout, plan_stderr = terraform.plan(
            capture_output=False,
            var={
                "cgid": cgid,
                "cg_whitelist": cg_whitelist,
                "profile": profile,
                "region": self.aws_region,
            },
            no_color=IsNotFlagged,
        )
        # For some reason, `python-terraform`'s `terraform init` returns "2" even
        # when it appears to succeed. For that reason, it will temporarily permit
        # retcode 2.
        if plan_retcode not in (0, 2):
            display_terraform_step_error("terraform plan", plan_retcode,
                                         plan_stdout, plan_stderr)
            return
        else:
            print(
                f"\n[cloudgoat] terraform plan completed with no error code.")

        apply_retcode, apply_stdout, apply_stderr = terraform.apply(
            capture_output=False,
            var={
                "cgid": cgid,
                "cg_whitelist": cg_whitelist,
                "profile": profile,
                "region": self.aws_region,
            },
            skip_plan=True,
            no_color=IsNotFlagged,
        )
        if apply_retcode != 0:
            display_terraform_step_error("terraform apply", apply_retcode,
                                         apply_stdout, apply_stderr)
            return
        else:
            print(
                f"\n[cloudgoat] terraform apply completed with no error code.")

        # python-terraform uses the '-json' flag by default.
        # The documentation for `output` suggests using output_cmd to receive the
        # library's standard threeple return value.
        # Can't use capture_output here because we need to write stdout to a file.
        output_retcode, output_stdout, output_stderr = terraform.output_cmd()

        if output_retcode != 0:
            display_terraform_step_error("terraform output", output_retcode,
                                         output_stdout, output_stderr)
            return
        else:
            print(
                f"\n[cloudgoat] terraform output completed with no error code."
            )

        # Within this output will be values that begin with "cloudgoat_output".
        # Each line of console output which contains this tag will be written into
        # a text file named "start.txt" in the scenario-instance folder.
        start_file_path = os.path.join(scenario_instance_dir_path, "start.txt")
        with open(start_file_path, "w") as start_file:
            for line in output_stdout.split("\n"):
                if line.count("cloudgoat_output") != 0:
                    start_file.write(line + "\n")

        print(
            f"\n[cloudgoat] Output file written to:\n\n    {start_file_path}\n"
        )
コード例 #5
0
ファイル: tests.py プロジェクト: security-geeks/cloudgoat
    def test_normalize_scenario_name(self):
        # Edge cases
        self.assertEqual(normalize_scenario_name(""), "")
        self.assertEqual(normalize_scenario_name("/"), "")
        self.assertEqual(normalize_scenario_name("/////"), "")

        # Simple cases, fake scenario names
        self.assertEqual(normalize_scenario_name("test_a/"), "test_a")
        self.assertEqual(normalize_scenario_name("/test_b"), "test_b")
        self.assertEqual(normalize_scenario_name("test_a/test_b"), "test_b")
        self.assertEqual(normalize_scenario_name("/test_a/test_b"), "test_b")

        # "scenarios" directory
        self.assertEqual(normalize_scenario_name("scenarios"), "scenarios")
        self.assertEqual(normalize_scenario_name("scenarios/"), "scenarios")
        self.assertEqual(normalize_scenario_name("/scenarios"), "scenarios")
        self.assertEqual(normalize_scenario_name("test_a/scenarios"),
                         "scenarios")
        self.assertEqual(normalize_scenario_name("scenarios/test_b"), "test_b")
        self.assertEqual(normalize_scenario_name("test_a/scenarios/test_b"),
                         "test_b")

        # Real scenario names
        self.assertEqual(normalize_scenario_name("rce_web_app/"),
                         "rce_web_app")
        self.assertEqual(normalize_scenario_name("/rce_web_app"),
                         "rce_web_app")

        self.assertEqual(normalize_scenario_name("scenarios/rce_web_app"),
                         "rce_web_app")
        self.assertEqual(normalize_scenario_name("/scenarios/rce_web_app"),
                         "rce_web_app")

        # Long paths
        self.assertEqual(
            normalize_scenario_name("/long/path/scenarios/rce_web_app"),
            "rce_web_app")
        self.assertEqual(
            normalize_scenario_name("scenarios/rce_web_app/even/longer/path"),
            "rce_web_app",
        )
        self.assertEqual(
            normalize_scenario_name(
                "/long/path/scenarios/rce_web_app/even/longer/path"),
            "rce_web_app",
        )

        self.assertEqual(
            normalize_scenario_name(
                "/long/path/scenarios/not-a-real-scenario"),
            "not-a-real-scenario",
        )
        self.assertEqual(
            normalize_scenario_name(
                "scenarios/not-a-real-scenario/even/longer/path"),
            "not-a-real-scenario",
        )
        self.assertEqual(
            normalize_scenario_name(
                "/long/path/scenarios/not-a-real-scenario/even/longer/path"),
            "not-a-real-scenario",
        )

        # Scenario instance paths
        self.assertEqual(
            normalize_scenario_name("codebuild_secrets_cgid0123456789"),
            "codebuild_secrets",
        )
        self.assertEqual(
            normalize_scenario_name(
                "scenarios/codebuild_secrets_cgid0123456789"),
            "codebuild_secrets",
        )
        self.assertEqual(
            normalize_scenario_name(
                "codebuild_secrets_cgid0123456789/scenarios"),
            "codebuild_secrets",
        )

        self.assertEqual(
            normalize_scenario_name(
                "/long/path/scenarios/codebuild_secrets_cgid0123456789"),
            "codebuild_secrets",
        )
        self.assertEqual(
            normalize_scenario_name(
                "scenarios/codebuild_secrets_cgid0123456789/even/longer/path"),
            "codebuild_secrets",
        )
        self.assertEqual(
            normalize_scenario_name(
                "/long/path/scenarios/codebuild_secrets_cgid0123456789/even/longer/path"
            ),
            "codebuild_secrets",
        )