From 393862df54b3aa161ab20e7cd48188c1c8ef682b Mon Sep 17 00:00:00 2001 From: Miroslav Shubernetskiy Date: Thu, 5 Sep 2024 13:00:08 -0400 Subject: [PATCH] feat: adding support for CHALK_BYPASS environment variable this allows to completely skip chalk when wrapping `docker`. Whenever its enabled, chalk directly proxies the command to `docker` without invoking any of the chalk internal machinery such as loading configs/etc --- CHANGELOG.md | 3 ++ src/bypass.nim | 37 ++++++++++++++++++++ src/chalk.nim | 4 ++- src/util.nim | 5 +-- tests/functional/data/configs/docker_cmd.c4m | 1 + tests/functional/test_docker.py | 7 ++-- 6 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 src/bypass.nim diff --git a/CHANGELOG.md b/CHANGELOG.md index b1817ef5..67dc5c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - `_OP_CLOUD_SYS_VENDOR` key for reporting sys vendor file content used to identity cloud provider. ([#418](https://github.com/crashappsec/chalk/pull/418)) +- Supporting `CHALK_BYPASS` environment variable which + bypasses chalk altogether when chalk wraps `docker`. + ([#419](https://github.com/crashappsec/chalk/pull/419)) ## 0.4.12 diff --git a/src/bypass.nim b/src/bypass.nim new file mode 100644 index 00000000..cacbde32 --- /dev/null +++ b/src/bypass.nim @@ -0,0 +1,37 @@ +## +## Copyright (c) 2024, Crash Override, Inc. +## +## This file is part of Chalk +## (see https://crashoverride.com/docs/chalk) +## + +import std/[os, strutils] +import pkg/nimutils/[file] +import "."/[util] + +proc bypassChalk*() = + # bypass any chalk execution if possible and directly exec into underlying command + # note this function explicitly writes to stderr for "logging" not to invoke + # any of the con4m machinery + if getEnv("CHALK_BYPASS").toLower() notin @["1", "true", "on"]: + return + + let + (path, exe) = getAppFilename().splitPath() + # setup.sh for example stores chalkless variants of binaries + # in chalkless folder + # we need to account for it as otherwise original binary + # might not be on PATH overwise + # and as we dont load the chalk config here as a precausion + # and so we must mimick disk structure direcly + chalklessDir = path.parentDir().joinPath("chalkless") + + case exe + of "docker": + let chalkless = file.findExePath(exe, extraPaths = @[chalklessDir]) + if chalkless == "": + stderr.writeLine("Could not find " & exe & " to exec while bypassing chalk") + quitChalk(1) + handleExec(@[chalkless], commandLineParams(), log = false) + else: + return diff --git a/src/chalk.nim b/src/chalk.nim index c89ffb6c..2dbab769 100644 --- a/src/chalk.nim +++ b/src/chalk.nim @@ -7,10 +7,12 @@ # Note that imports cause topics and plugins to register. {.warning[UnusedImport]: off.} -import "."/[config, confload, commands, norecurse, sinks, +import "."/[bypass, config, confload, commands, norecurse, sinks, attestation_api, util] when isMainModule: + bypassChalk() + setupSignalHandlers() # util.nim setupTerminal() # util.nim ioSetup() # sinks.nim diff --git a/src/util.nim b/src/util.nim index 86a64d48..e992d001 100644 --- a/src/util.nim +++ b/src/util.nim @@ -399,10 +399,11 @@ proc findExePath*(cmdName: string, trace("Found '" & cmdName & "' in PATH: " & foundExes[0]) return some(foundExes[0]) -proc handleExec*(prioritizedExes: seq[string], args: seq[string]) {.noreturn.} = +proc handleExec*(prioritizedExes: seq[string], args: seq[string], log = true) {.noreturn.} = for path in prioritizedExes: let cargs = allocCStringArray(@[path] & args) - trace("execv: " & path & " " & args.join(" ")) + if log: + trace("execv: " & path & " " & args.join(" ")) discard execv(cstring(path), cargs) # Either execv doesn't return, or something went wrong. No need to check the # error code. diff --git a/tests/functional/data/configs/docker_cmd.c4m b/tests/functional/data/configs/docker_cmd.c4m index 46c30ce0..4557c5fb 100644 --- a/tests/functional/data/configs/docker_cmd.c4m +++ b/tests/functional/data/configs/docker_cmd.c4m @@ -1 +1,2 @@ default_command: "docker" +error("chalk is wrapping docker") diff --git a/tests/functional/test_docker.py b/tests/functional/test_docker.py index 6f95d93b..7daa5a18 100644 --- a/tests/functional/test_docker.py +++ b/tests/functional/test_docker.py @@ -1344,14 +1344,17 @@ def test_docker_diff_user(chalk_default: Chalk): assert result -def test_docker_default_command(chalk_copy: Chalk, tmp_data_dir: Path): +@pytest.mark.parametrize("bypass", [True, False]) +def test_docker_default_command(chalk_copy: Chalk, tmp_data_dir: Path, bypass: bool): assert chalk_copy.load(CONFIGS / "docker_cmd.c4m") expected = Docker.version() docker = tmp_data_dir / "docker" shutil.copy(chalk_copy.binary, docker) - actual = run([str(docker), "--version"]) + actual = run([str(docker), "--version"], env={"CHALK_BYPASS": str(bypass)}) assert actual assert actual.text == expected.text + if bypass: + assert actual.logs == expected.logs def test_version_bare(chalk_default: Chalk):