Skip to content
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

fix: error docker inspect logs during chalk exec #248

Merged
merged 7 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- [ ] Followed the steps in the contributor's guide: https://crashoverride.com/docs/other/contributing#filing-the-pull-request
- [ ] PR title uses [semantic commit messages](https://nitayneeman.com/posts/understanding-semantic-commit-messages-using-git-and-angular/#fix)
- [ ] Filled out the template to a useful degree
- [ ] Updated `CHANGELOG.md` if necessary

## Issue

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
`AWS_DEFAULT_REGION` environment variables were not set
[#246](https://github.com/crashappsec/chalk/pull/246)

### Fixes

- The Docker codec is now bypassed when `docker` is not
installed. Previously, any chalk sub-scan such as
during `chalk exec` had misleading error logs.
[#248](https://github.com/crashappsec/chalk/pull/248)

## 0.3.4

**Mar 18, 2024**
Expand Down
2 changes: 1 addition & 1 deletion src/chalk_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type
c: Option[string]) {.cdecl.}
Plugin* = ref object
name*: string
enabled*: bool
configInfo*: PluginSpec
getChalkTimeHostInfo*: ChalkTimeHostCb
getChalkTimeArtifactInfo*: ChalkTimeArtifactCb
Expand Down Expand Up @@ -412,7 +413,6 @@ var
chalkConfig*: ChalkConfig
con4mRuntime*: ConfigStack
commandName*: string
dockerExeLocation*: string = ""
gitExeLocation*: string = ""
sshKeyscanExeLocation*: string = ""

Expand Down
22 changes: 14 additions & 8 deletions src/commands/cmd_docker.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ proc runMungedDockerInvocation(ctx: DockerInvocation): int =
var
newStdin = "" # Indicated passthrough.
args = ctx.newCmdLine
exe = getDockerExeLocation()

trace("Running docker: " & dockerExeLocation & " " & args.join(" "))
trace("Running docker: " & exe & " " & args.join(" "))

if ctx.dfPassOnStdin:
if not ctx.inDockerFile.endswith("\n"):
ctx.inDockerFile &= "\n"
newStdin = ctx.addInstructions(ctx.inDockerFile)
trace("Passing on stdin: \n" & newStdin)

result = runCmdNoOutputCapture(dockerExeLocation, args, newStdin)
result = runCmdNoOutputCapture(exe, args, newStdin)

proc doReporting*(topic: string){.importc.}

Expand Down Expand Up @@ -508,6 +509,7 @@ proc runBuild(ctx: DockerInvocation): int =
chalk.marked = true

proc runPush(ctx: DockerInvocation): int =
let exe = getDockerExeLocation()
if ctx.cmdBuild:
var tags = ctx.foundTags
if len(tags) == 0:
Expand All @@ -516,7 +518,7 @@ proc runPush(ctx: DockerInvocation): int =
for tag in tags:
trace("docker pushing: " & tag)
ctx.newCmdLine = @["push", tag]
result = runCmdNoOutputCapture(dockerExeLocation, ctx.newCmdLine)
result = runCmdNoOutputCapture(exe, ctx.newCmdLine)
if result != 0:
break

Expand All @@ -533,9 +535,10 @@ proc runPush(ctx: DockerInvocation): int =

# Here, if we fail, there's no re-run.
# We ran their original command line so there is nothing to fall back on.
return runCmdNoOutputCapture(dockerExeLocation, ctx.newCmdLine)
return runCmdNoOutputCapture(exe, ctx.newCmdLine)

proc createAndPushManifest(ctx: DockerInvocation, platforms: seq[string]): int =
let exe = getDockerExeLocation()
# not a multi-platform build so manifest should not be used
if len(platforms) < 2:
return 0
Expand All @@ -546,7 +549,7 @@ proc createAndPushManifest(ctx: DockerInvocation, platforms: seq[string]): int =
platformTags.add(ctx.getTagForPlatform(tag, platform))

let exitCode = runCmdNoOutputCapture(
dockerExeLocation,
exe,
@["buildx", "imagetools", "create", "-t"] & platformTags,
)
if exitCode != 0:
Expand All @@ -561,9 +564,10 @@ proc createAndPushManifest(ctx: DockerInvocation, platforms: seq[string]): int =
# TODO: Any other noteworthy commands to wrap (run, etc)

template passThroughLogic() =
let exe = getDockerExeLocation()
try:
# Silently pass through other docker commands right now.
exitCode = runCmdNoOutputCapture(dockerExeLocation, args)
exitCode = runCmdNoOutputCapture(exe, args)
if chalkConfig.dockerConfig.getReportUnwrappedCommands():
reporting.doReporting("report")
except:
Expand Down Expand Up @@ -707,14 +711,16 @@ template postDockerActivity() =
exitCode = 0

proc runCmdDocker*(args: seq[string]) =
setDockerExeLocation()

var
exitCode = 0
ctx = args.processDockerCmdLine()

ctx.originalArgs = args

if getDockerExeLocation() == "":
error("docker command is missing. chalk requires docker binary installed to wrap docker commands.")
ctx.dockerFailSafe()

if ctx.cmdBuild:
# Build with --push is still a build operation.
setCommandName("build")
Expand Down
7 changes: 1 addition & 6 deletions src/commands/cmd_extract.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

## The `chalk extract` command.

import ".."/[config, collect, reporting, plugins/codecDocker, plugin_api,
docker_base]
import ".."/[config, collect, reporting, plugins/codecDocker, plugin_api]

template processDockerChalkList(chalkList: seq[ChalkObj]) =
for item in chalkList:
Expand Down Expand Up @@ -57,26 +56,22 @@ template coreExtractContainers() =


proc runCmdExtract*(path: seq[string]) {.exportc,cdecl.} =
setDockerExeLocation()
setContextDirectories(path)
initCollection()
coreExtractFiles(path)
doReporting()

proc runCmdExtractImages*() =
setDockerExeLocation()
initCollection()
coreExtractImages()
doReporting()

proc runCmdExtractContainers*() =
setDockerExeLocation()
initCollection()
coreExtractContainers()
doReporting()

proc runCmdExtractAll*(path: seq[string]) =
setDockerExeLocation()
setContextDirectories(path)
initCollection()
coreExtractFiles(path)
Expand Down
29 changes: 17 additions & 12 deletions src/docker_base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import std/[httpclient, uri]
import "."/[config, dockerfile, util, reporting, semver, www_authenticate]

var
buildXVersion: Version = parseVersion("0")
dockerVersion: Version = parseVersion("0")
buildXVersion = parseVersion("0")
dockerVersion = parseVersion("0")
dockerExeLocation = ""

const
hashHeader* = "sha256:"

var dockerPathOpt: Option[string] = none(string)

template extractDockerHash*(value: string): string =
if not value.startsWith(hashHeader):
value
Expand All @@ -29,24 +28,25 @@ template extractDockerHash*(value: string): string =
template extractBoxedDockerHash*(value: Box): Box =
pack(extractDockerHash(unpack[string](value)))

proc setDockerExeLocation*() =
proc getDockerExeLocation*(): string =
once:
trace("Searching PATH for 'docker'")
let
dockerConfigPath = chalkConfig.getDockerExe()
dockerExeOpt = findExePath("docker",
configPath = dockerConfigPath,
configPath = dockerConfigPath,
ignoreChalkExes = true)
dockerExeLocation = dockerExeOpt.get("")
if dockerExeLocation == "":
warn("No docker command found in PATH. `chalk docker` not available.")
return dockerExeLocation

proc runDockerGetEverything*(args: seq[string], stdin = "", silent = true): ExecOutput =
let exe = getDockerExeLocation()
if not silent:
trace("Running docker: " & dockerExeLocation & " " & args.join(" "))
trace("Running docker: " & exe & " " & args.join(" "))
if stdin != "":
trace("Passing on stdin: \n" & stdin)
result = runCmdGetEverything(dockerExeLocation, args, stdin)
result = runCmdGetEverything(exe, args, stdin)
if not silent and result.exitCode > 0:
trace(strutils.strip(result.stderr & result.stdout))
return result
Expand Down Expand Up @@ -121,9 +121,14 @@ proc dockerFailsafe*(info: DockerInvocation) {.cdecl, exportc.} =
if info.dockerFileLoc == ":stdin:":
newStdin = info.inDockerFile

let exitCode = runCmdNoOutputCapture(dockerExeLocation,
info.originalArgs,
newStdin)
let
exe = getDockerExeLocation()
# even if docker is not found call subprocess with valid command name
# so that we can bubble up error from subprocess
docker = if exe != "": exe else: "docker"
exitCode = runCmdNoOutputCapture(docker,
info.originalArgs,
newStdin)
doReporting("fail")
quitChalk(exitCode)

Expand Down
21 changes: 18 additions & 3 deletions src/plugin_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import "."/[config, chalkjson, util]
# should all be pre-checked.

proc callGetChalkTimeHostInfo*(plugin: Plugin): ChalkDict =
if not plugin.enabled:
return ChalkDict()

let cb = plugin.getChalkTimeHostInfo

# explicit callback check - otherwise it results in segfault
Expand All @@ -31,6 +34,9 @@ proc callGetChalkTimeHostInfo*(plugin: Plugin): ChalkDict =

proc callGetChalkTimeArtifactInfo*(plugin: Plugin, obj: ChalkObj):
ChalkDict =
if not plugin.enabled:
return ChalkDict()

let cb = plugin.getChalkTimeArtifactInfo

# explicit callback check - otherwise it results in segfault
Expand All @@ -42,6 +48,9 @@ proc callGetChalkTimeArtifactInfo*(plugin: Plugin, obj: ChalkObj):

proc callGetRunTimeArtifactInfo*(plugin: Plugin, obj: ChalkObj, b: bool):
ChalkDict =
if not plugin.enabled:
return ChalkDict()

let cb = plugin.getRunTimeArtifactInfo

# explicit callback check - otherwise it results in segfault
Expand All @@ -53,6 +62,9 @@ proc callGetRunTimeArtifactInfo*(plugin: Plugin, obj: ChalkObj, b: bool):

proc callGetRunTimeHostInfo*(plugin: Plugin, objs: seq[ChalkObj]):
ChalkDict =
if not plugin.enabled:
return ChalkDict()

let cb = plugin.getRunTimeHostInfo

# explicit callback check - otherwise it results in segfault
Expand Down Expand Up @@ -594,7 +606,8 @@ proc newPlugin*(
getChalkTimeArtifactInfo: ctArtCallback,
getRunTimeArtifactInfo: rtArtCallback,
getRunTimeHostInfo: rtHostCallback,
internalState: cache)
internalState: cache,
enabled: true)

if not result.checkPlugin(codec = false):
result = Plugin(nil)
Expand All @@ -612,7 +625,8 @@ proc newCodec*(
handleWrite: HandleWriteCb = HandleWritecb(defaultCodecWrite),
nativeObjPlatforms: seq[string] = @[],
cache: RootRef = RootRef(nil),
commentStart: string = "#"):
commentStart: string = "#",
enabled: bool = true):
Plugin {.discardable, cdecl.} =

result = Plugin(name: name,
Expand All @@ -627,7 +641,8 @@ proc newCodec*(
handleWrite: handleWrite,
nativeObjPlatforms: nativeObjPlatforms,
internalState: cache,
commentStart: commentStart)
commentStart: commentStart,
enabled: enabled)

if not result.checkPlugin(codec = true):
result = Plugin(nil)
14 changes: 9 additions & 5 deletions src/plugins/codecDocker.nim
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
##
import ".."/[config, docker_base, chalkjson, attestation_api, plugin_api, util]

const
markFile = "chalk.json"
markLocation = "/chalk.json"
const markLocation = "/chalk.json"

proc dockerGetChalkId*(self: Plugin, chalk: ChalkObj): string {.cdecl.} =
if chalk.extract != nil and "CHALK_ID" in chalk.extract:
Expand Down Expand Up @@ -530,7 +528,7 @@ proc inspectContainer(chalk: ChalkObj) =
cmdOut = runDockerGetEverything(@["container", "inspect", id])

if cmdout.getExit() != 0:
warn(chalk.userRef & ": Container inspection failed (no longer running?)")
warn(chalk.userRef & ": Could not perform container inspection (no longer running?)")
return

let
Expand Down Expand Up @@ -601,6 +599,12 @@ proc dockerExtractChalkMark*(chalk: ChalkObj): ChalkDict {.exportc, cdecl.} =
addUnmarked(chalk.name)

proc loadCodecDocker*() =
# cant use getDockerExePath as that uses codecs to ignore chalk
# wrappings hence we just check if anything docker is on PATH here
let enabled = nimutils.findExePath("docker") != ""
if not enabled:
warn("Disabling docker codec as docker command is not available")
newCodec("docker",
rtArtCallback = RunTimeArtifactCb(dockerGetRunTimeArtifactInfo),
getChalkId = ChalkIdCb(dockerGetChalkId))
getChalkId = ChalkIdCb(dockerGetChalkId),
enabled = enabled)
6 changes: 3 additions & 3 deletions src/util.nim
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ proc findExePath*(cmdName: string,
# takes precedence over rest of dirs in PATH
paths = @[configPath.get()] & paths

trace("Searching path for " & cmdName)
trace("Searching PATH for " & cmdName)
var foundExes = findAllExePaths(cmdName, paths, usePath)

if ignoreChalkExes:
Expand All @@ -386,10 +386,10 @@ proc findExePath*(cmdName: string,
foundExes = newExes

if foundExes.len() == 0:
trace("Could not find '" & cmdName & "' in path.")
trace("Could not find '" & cmdName & "' in PATH.")
return none(string)

trace("Found '" & cmdName & "' in path: " & foundExes[0])
trace("Found '" & cmdName & "' in PATH: " & foundExes[0])
return some(foundExes[0])

proc handleExec*(prioritizedExes: seq[string], args: seq[string]) {.noreturn.} =
Expand Down
2 changes: 1 addition & 1 deletion tests/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ RUN mkdir -p /.cache/pypoetry \
COPY pyproject.toml poetry.lock $WORKDIR/
RUN poetry install --no-plugins

COPY --from=gcr.io/projectsigstore/cosign /ko-app/cosign /usr/local/bin/cosign
COPY --from=gcr.io/projectsigstore/cosign:v2.2.3 /ko-app/cosign /usr/local/bin/cosign
Copy link
Contributor

@ee7 ee7 Mar 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For visibility: this change is unrelated to this PR, and is because #254 was incomplete.

I will create an issue for this. The checks passed successfully on #254 and the corresponding commit on main (f47aa02) only because the tests workflow was skipped, but it isn't skipped on this PR.

Edit: created #255.

COPY --from=docker:24 /usr/local/bin/docker /usr/local/bin/docker
COPY --from=docker/buildx-bin:0.11.2 /buildx /usr/lib/docker/cli-plugins/docker-buildx

Expand Down
Loading