-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Created 3 tests for containers #4
Changes from 3 commits
b11d5f5
de2ff67
0fa3a93
64f736b
9d34c13
f57077b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,7 @@ | |
|
||
|
||
# https://docs.docker.com/engine/reference/commandline/ps/ | ||
class ContainerState(enum.StrEnum): | ||
class ContainerState(enum.Enum): | ||
CREATED = "created" | ||
RESTARTING = "restarting" | ||
RUNNING = "running" | ||
|
@@ -39,7 +39,7 @@ def is_done(self): | |
class ScoutConfig: | ||
credentials_file: pathlib.Path | ||
output_dir: pathlib.Path | ||
docker_image: str = "rossja/ncc-scoutsuite:aws-latest" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should keep the default image as the default value for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK! |
||
docker_image: str | ||
docker_poll_interval: float = 16.0 | ||
docker_socket: str | None = None | ||
docker_timeout: int = 5 | ||
|
@@ -52,6 +52,8 @@ class Task(Protocol): | |
@dataclasses.dataclass(frozen=True) | ||
class ScoutTask: | ||
label: str | ||
commands: str | None = None | ||
volumes: dict | None = None | ||
aws_api_key: str | None = None | ||
aws_api_secret: str | None = None | ||
role_arn: str | None = None | ||
|
@@ -63,8 +65,10 @@ class ScoutTask: | |
class ScanModule(Protocol): | ||
def __init__(self, config: Any, callback: TaskCompletionCallback) -> None: | ||
... | ||
|
||
def enqueue(self, taskcfg: Task) -> None: | ||
... | ||
|
||
def shutdown(self, wait: bool) -> None: | ||
... | ||
|
||
|
@@ -90,36 +94,18 @@ def get_docker_client() -> docker.DockerClient: | |
|
||
def enqueue(self, taskcfg: Task) -> None: | ||
assert isinstance(taskcfg, ScoutTask) | ||
|
||
outfp = self.config.output_dir / taskcfg.label | ||
os.makedirs(outfp, exist_ok=True) | ||
try: | ||
ctx = self.docker.containers.run( | ||
self.config.docker_image, | ||
command=[ | ||
cunha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"scout", | ||
"aws", | ||
"--no-browser", | ||
"--result-format", | ||
"json", | ||
"--report-dir", | ||
f"{OUTDIR_CONTAINER_MOUNT}", | ||
"--logfile", | ||
f"{OUTDIR_CONTAINER_MOUNT}/scout.log", | ||
], | ||
command=taskcfg.commands, | ||
detach=True, | ||
labels={SCOUT_TASK_LABEL_KEY: taskcfg.label}, | ||
stdout=True, | ||
stderr=True, | ||
volumes={ | ||
str(self.config.credentials_file): { | ||
cunha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"bind": "/root/.aws/credentials", | ||
"mode": "ro", | ||
}, | ||
str(outfp): { | ||
"bind": OUTDIR_CONTAINER_MOUNT, | ||
"mode": "rw", | ||
}, | ||
}, | ||
volumes=taskcfg.volumes, | ||
working_dir="/root", | ||
) | ||
except docker.errors.APIError as e: | ||
|
@@ -132,14 +118,16 @@ def enqueue(self, taskcfg: Task) -> None: | |
def shutdown(self, wait: bool = True) -> None: | ||
logging.info("Scout shutting down (wait=%s)", wait) | ||
self.running = False | ||
self.handle_finished_containers() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should handle any successfully-finished containers before calling |
||
if not wait: | ||
with self.lock: | ||
for ctx, cfg in self.containers: | ||
logging.warning("Force-closing container for task %s", cfg.label) | ||
ctx.remove(force=True) | ||
logging.info("Joining Scout polling thread") | ||
self.thread.join() | ||
Sacramento-20 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.containers.clear() | ||
else: | ||
self.handle_finished_containers() | ||
self.thread.join() | ||
|
||
logging.info("Joined Scout polling thread, module shut down") | ||
|
||
def scout_polling_thread(self) -> None: | ||
|
@@ -152,32 +140,43 @@ def handle_finished_containers(self) -> None: | |
completed = set() | ||
with self.lock: | ||
for ctx, cfg in self.containers: | ||
ctx.reload() | ||
try: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was hoping we wouldn't need the try/catch if we remove the container inside |
||
ctx.reload() | ||
except docker.errors.NotFound: | ||
logging.warning("Container not found: %s", cfg.label) | ||
continue | ||
|
||
if not ContainerState(ctx.status).is_done(): | ||
continue | ||
|
||
assert cfg.label == ctx.labels[SCOUT_TASK_LABEL_KEY] | ||
|
||
r = ctx.wait(timeout=self.config.docker_timeout) | ||
r["stdout"] = ctx.logs( | ||
stdout=True, stderr=False, timestamps=True | ||
).decode("utf8") | ||
r["stderr"] = ctx.logs( | ||
stdout=False, stderr=True, timestamps=True | ||
).decode("utf8") | ||
|
||
outfp = self.config.output_dir / cfg.label | ||
with gzip.open(outfp / "result.json.gz", "wt", encoding="utf8") as fd: | ||
json.dump(r, fd) | ||
self.task_completion_callback(cfg.label, True) | ||
logging.info( | ||
"Scout run completed, id %s status %d", | ||
"Scout run completed, id %s sta+-tus %d", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved and will be included in the next commit. |
||
cfg.label, | ||
r[DOCKER_STATUSCODE_KEY], | ||
) | ||
completed.add((ctx, cfg)) | ||
|
||
self.containers -= completed | ||
|
||
logging.info( | ||
"Running %d ScoutSuite containers, waiting %d seconds to refresh", | ||
len(self.containers), | ||
self.config.docker_poll_interval, | ||
) | ||
|
||
for ctx, _cfg in completed: | ||
ctx.remove() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not equivalent to using
StrEnum
. Document that we should move to StrEnum if Python 3.11 is available.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StrEnum
not compatible with the current version, the change will be included in the documentation.