Skip to content

Commit

Permalink
fix: only wrapping ENTRYPOINT, if present (#112)
Browse files Browse the repository at this point in the history
* fix: only wrapping ENTRYPOINT, if present

previously we were wrapping either CMD/ENTRYPOINT however that is
incorrect behavior as it can change semantics of the image
if the base image has any custom ENTRYPOINT

For example official AWS lambda images have this problem
and their ENTRYPOINT requires specific CMD shape
and therefore we cannot adjust CMD without guaranteeing
container behavior does not change.

* fix(permissions): chalk runs for users without home directory

In some cases chalk can run for uid which does not have a home
directory such as in AWS lambda in which case chalk fails to resolve
all paths which have ~ in them.

This fixes that by ignoring those paths when resolvePath fails
and in some other cases like for temp files also checks that
tmp folder is writable.

* switching to json for ecs metadata

* fix: handling incorrect /chalk.json permissions

* fix(ecs): sending ECS metadata as json object

* feat(ecs): collecting more ecs metadata

this now includes all task containers metadata as well as stats (if present)

* feat(lambda): adding basic lambda metadata plugin

* refactor: removing readOneFile() to nimutils.tryToLoadFile()

* feat: extracting basic ecs metadata to dedicated keys

* adding logs for exec command

* feat: adding sink config priority field

this allows to prioritize some sinks higher in the reporting order

* bumping nimutils to remove debug help pager logs [ci skip]

* fix: detaching stdin tty from chalk child process during exec

otherwise we get `HUP` signal handling indicating there is an error
which is incorrect as HUP is sent as part of TTY handling logic

* fix: updating license check to honor years per file

limiting first year to 2023, even though some files were committed in 2022

* feat: adding wrap_cmd option to wrap images without ENTRYPOINT

* test: using unique path for each vendor test

this allows tests not be exclusive as otherwise there are conflicts
between tests

* fix: running external tools only on insert commands

* fix: skipping external tools on docker images

at chalk time image is not built yet so we cannot run external tools
on it yet

* feat: adding sink retries via nimutils/con4m

* fix: running external tools on context dir for docker builds
  • Loading branch information
miki725 authored Jan 4, 2024
1 parent a5570d8 commit 0b96fdd
Show file tree
Hide file tree
Showing 53 changed files with 1,147 additions and 282 deletions.
31 changes: 31 additions & 0 deletions .github/check_license.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh

set -e

args=
for arg; do
shift
case "$arg" in
--)
break
;;
*)
args="$args $arg"
;;
esac
done

for file; do
years=$(git log --follow --oneline --format='%aI' -- "$file" | cut -d- -f1 | tail -n1)
last=$(git log --follow --oneline --format='%aI' -- "$file" | cut -d- -f1 | head -n1)
if [ "$years" = "2022" ]; then
years=2023
fi
if [ "$last" != "$years" ]; then
years="$years-$last"
fi
if [ -z "$years" ]; then
years=$(date +%Y)
fi
licenseheaders --years="$years" $args "$file"
done
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Setup Python
uses: actions/setup-python@v3
Expand Down
16 changes: 9 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ repos:
- id: check-added-large-files
args: ["--maxkb=128"]

- repo: https://github.com/johann-petrak/licenseheaders
rev: 8e2d6f944aea639d62c8d26cd99dab4003c3904d
- repo: local
hooks:
- id: licenseheaders
name: chalkcopyright
name: licenseheaders
language: python
additional_dependencies:
- https://github.com/johann-petrak/licenseheaders/archive/8e2d6f944aea639d62c8d26cd99dab4003c3904d.zip
entry: ./.github/check_license.sh
files: \.(nim|c4m|c42spec|bash|py)$
args:
- --tmpl=.github/copyright.tmpl
- --current-year
# - --current-year
# - --years=2022,2023
- --projname=Chalk
- --projurl=https://crashoverride.com/docs/chalk
- --additional-extensions
- script=.nim,.c4m,.c42spec,.bash
- robot=.py # native python each run adds another newline
- --files
files: \.(nim|c4m|c42spec|bash|py)$
- --
# selfsigned is not our copyright
# data has test data for python encoding so we leave it alone
exclude: |
Expand All @@ -33,8 +37,6 @@ repos:
data/.*
)$
- repo: local
hooks:
- id: chalkversion
name: chalkversion
description: verify chalk version matches across all chalk files
Expand Down
2 changes: 1 addition & 1 deletion chalk.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ bin = @["chalk"]

# Dependencies
requires "nim >= 2.0.0"
requires "https://github.com/crashappsec/con4m#a067d04cd7c5e11b633c8d1a284c42076939637c"
requires "https://github.com/crashappsec/con4m#904639b9f51e655b908e221db26fbdd9fff382b3"
requires "https://github.com/viega/zippy == 0.10.7" # MIT
requires "https://github.com/aruZeta/QRgen == 3.0.0" # MIT

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ services:
security_opt:
- seccomp=unconfined # for gdb
volumes:
- .:/chalk
- $PWD:$PWD
- /var/run/docker.sock:/var/run/docker.sock
working_dir: /chalk/tests
working_dir: $PWD/tests
networks:
- chalk
- imds
Expand Down
8 changes: 4 additions & 4 deletions src/api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ proc refreshAccessToken*(refresh_token: string): string =
let response = client.safeRequest(url = refresh_url, httpMethod = HttpPost, body = $refresh_token)
client.close()

if response.status.startswith("200"):
if response.code.is2xx():
# parse json response and save / return values
let
jsonNode = parseJson(response.body())
Expand Down Expand Up @@ -107,7 +107,7 @@ proc getChalkApiToken*(): (string, string) =
response = client.safeRequest(url = login_url, httpMethod = HttpPost, body = "")
client.close()

if response.status.startswith("200"):
if response.code.is2xx():
# parse json response and save / return values
let jsonNode = parseJson(response.body())
authId = jsonNode["id"].getStr()
Expand Down Expand Up @@ -139,7 +139,7 @@ proc getChalkApiToken*(): (string, string) =
clientPoll.close()

# check response - HTTP 200 = yes, HTTP 428 = Not yet
if responsePoll.status.startswith("200"):
if responsePoll.code.is2xx():
authnSuccess = true
eraseLine()
stdout.write(succFr)
Expand All @@ -155,7 +155,7 @@ proc getChalkApiToken*(): (string, string) =
pollPayloadBase64 = jwtSplitAndDecode($accessToken, true)
ret = ($accessToken, $refreshToken)

elif responsePoll.status.startswith("428") or responsePoll.status.startswith("403"):
elif responsePoll.code in [Http428, Http403]:
# sleep for requested polling period while showing spinner before polling again

# restart spinner animation - reset vars
Expand Down
31 changes: 15 additions & 16 deletions src/attestation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
## (see https://crashoverride.com/docs/chalk)
##

import api, base64, chalkjson, config, httpclient, net, os, selfextract,
uri, nimutils/sinks
import api, base64, chalkjson, config, httpclient, net, os, selfextract, uri

const
attestationObfuscator = staticExec(
Expand Down Expand Up @@ -110,7 +109,7 @@ template callTheSecretService(base: string, prKey: string, apiToken: string, bod
client: HttpClient
context: SslContext
response: Response

# This is the id that will be used to identify the secret in the API
signingID = sha256Hex(attestationObfuscator & prkey)

Expand Down Expand Up @@ -162,9 +161,9 @@ proc saveToSecretManager*(content: string, prkey: string, apiToken: string): boo
response = callTheSecretService(base, prkey, apiToken, body, HttpPut)

trace("Sending encrypted secret: " & body)
if response.status.startswith("405"):
if response.code == Http405:
info("This secret is already saved.")
elif response.status[0] != '2':
elif not response.code.is2xx():
error("When attempting to save signing secret: " & response.status)
trace(response.body())
return false
Expand All @@ -185,9 +184,9 @@ proc loadFromSecretManager*(prkey: string, apikey: string): bool =

let response = callTheSecretService(base, prKey, apikey, "", HttpGet)

if response.status[0] != '2':
if not response.code.is2xx():
# authentication issue / token expiration - begin reauth
if response.status.startswith("401"):
if response.code == Http401:
# parse json response and save / return values()
let jsonNodeReason = parseJson(response.body())
let reasonCode = jsonNodeReason["Message"].getStr()
Expand All @@ -197,7 +196,7 @@ proc loadFromSecretManager*(prkey: string, apikey: string): bool =
# Remove current API token from self chalk mark
selfChalk.extract["$CHALK_API_KEY"] = pack("")

# refresh access_token
# refresh access_token
let boxedOptRefresh = selfChalkGetKey("$CHALK_API_REFRESH_TOKEN")
if boxedOptRefresh.isSome():
let
Expand All @@ -215,7 +214,7 @@ proc loadFromSecretManager*(prkey: string, apikey: string): bool =
return loadFromSecretManager(prkey, $newApiToken)
else:
warn("Could not retrieve signing secret: " & response.status & "\n" &
"Will not be able to sign / verify.")
"Will not be able to sign / verify.")
return false

var
Expand All @@ -232,7 +231,7 @@ proc loadFromSecretManager*(prkey: string, apikey: string): bool =
raise newException(ValueError, "Nice hex, but wrong size.")
except:
error("When loading the signing secret, received an invalid " &
"response from server: " & response.status)
"response from server: " & response.status)
return false

trace("Successfully retrieved secret from secret manager.")
Expand Down Expand Up @@ -324,7 +323,7 @@ proc commitPassword(pri, apiToken: string, gen: bool) =

if gen:
printIt = true

else:
let idString = "The ID of the backed up key is: " & $signingID
info(idString)
Expand Down Expand Up @@ -542,20 +541,20 @@ proc attemptToLoadKeys*(silent=false): bool =
return true

proc attemptToGenKeys*(): bool =
var
var
apiToken = ""
refreshToken = ""
let use_api = chalkConfig.getApiLogin()

if use_api:
# Possible we already have API keys chalked into ourself
# refresh token
# refresh token
let boxedOptRefresh = selfChalkGetKey("$CHALK_API_REFRESH_TOKEN")
if boxedOptRefresh.isSome():
let boxedRefresh = boxedOptRefresh.get()
refreshToken = unpack[string](boxedRefresh)
trace("Refresh token retrieved from chalk mark: " & $refreshToken)

# access_token
let boxedOptAccess = selfChalkGetKey("$CHALK_API_KEY")
if boxedOptAccess.isSome():
Expand All @@ -564,10 +563,10 @@ proc attemptToGenKeys*(): bool =
trace("Access token retrieved from chalk mark: " & $apiToken)
else:
trace("empty access token")

else:
trace("empty refresh token")

if apiToken == "" or refreshToken == "":
# could not retreive so requesting new
trace("Missing token, starting new login..." & apiToken & refreshToken)
Expand Down
13 changes: 7 additions & 6 deletions src/collect.nim
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ proc collectChalkTimeHostInfo*() =
hostInfo[k] = v
except:
warn("When collecting chalk-time host info, plugin implementation " &
plugin.name & " threw an exception it didn't handle.")
plugin.name & " threw an exception it didn't handle: " & getCurrentExceptionMsg())
dumpExOnDebug()

proc initCollection*() =
Expand Down Expand Up @@ -143,8 +143,8 @@ proc collectRunTimeArtifactInfo*(artifact: ChalkObj) =
trace(plugin.name & ": Plugin called.")
except:
warn("When collecting run-time artifact data, plugin implementation " &
plugin.name & " threw an exception it didn't handle (artifact = " &
artifact.name & ".")
plugin.name & " threw an exception it didn't handle (artifact = " &
artifact.name & "): " & getCurrentExceptionMsg())
dumpExOnDebug()

let hashOpt = artifact.callGetEndingHash()
Expand Down Expand Up @@ -196,8 +196,8 @@ proc collectChalkTimeArtifactInfo*(obj: ChalkObj) =
trace(plugin.name & ": Plugin called.")
except:
warn("When collecting chalk-time artifact data, plugin implementation " &
plugin.name & " threw an exception it didn't handle (artifact = " &
obj.name & ".")
plugin.name & " threw an exception it didn't handle (artifact = " &
obj.name & "): " & getCurrentExceptionMsg())
dumpExOnDebug()

proc collectRunTimeHostInfo*() =
Expand All @@ -218,7 +218,8 @@ proc collectRunTimeHostInfo*() =
hostInfo[k] = v
except:
warn("When collecting run-time host info, plugin implementation " &
plugin.name & " threw an exception it didn't handle.")
plugin.name & " threw an exception it didn't handle: " &
getCurrentExceptionMsg())
dumpExOnDebug()


Expand Down
Loading

0 comments on commit 0b96fdd

Please sign in to comment.