harden production debug wrapper
This commit is contained in:
parent
0768f6d783
commit
d8dcb84edb
3 changed files with 24 additions and 5 deletions
|
|
@ -45,6 +45,10 @@ on the diagnostics client at:
|
||||||
- Preserve the `restrict` and forced-command options in `authorized_keys`.
|
- Preserve the `restrict` and forced-command options in `authorized_keys`.
|
||||||
- Verify arbitrary commands and malformed arguments are denied.
|
- Verify arbitrary commands and malformed arguments are denied.
|
||||||
- Do not print container environment variables or application secrets.
|
- Do not print container environment variables or application secrets.
|
||||||
|
- Ensure `status` passes its token to curl through stdin, not an argv `-H`
|
||||||
|
argument visible through `/proc`.
|
||||||
|
- Resolve the container into a variable before Docker calls so selector failure
|
||||||
|
propagates with the documented return code.
|
||||||
|
|
||||||
The account may run only:
|
The account may run only:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,11 @@ Security invariants:
|
||||||
fail closed.
|
fail closed.
|
||||||
- Verify denials as well as successful commands. Audit records are available via
|
- Verify denials as well as successful commands. Audit records are available via
|
||||||
`journalctl -t chart-debug`.
|
`journalctl -t chart-debug`.
|
||||||
|
- Keep secrets off process command lines. The status command supplies
|
||||||
|
`CHART_AUTH_TOKEN` to curl through stdin (`curl --config -`), never `-H`.
|
||||||
|
- Assign `resolve_container` before invoking Docker. Calling it inline through
|
||||||
|
command substitution can trap its exit in a subshell and obscure the intended
|
||||||
|
fail-closed return code.
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ case "${args[0]}" in
|
||||||
[[ "${args[2]}" =~ ^[1-9][0-9]*(s|m|h|d)$ ]] || {
|
[[ "${args[2]}" =~ ^[1-9][0-9]*(s|m|h|d)$ ]] || {
|
||||||
echo "invalid duration" >&2; exit 2;
|
echo "invalid duration" >&2; exit 2;
|
||||||
}
|
}
|
||||||
docker logs --timestamps --tail "$MAX_LOG_LINES" --since "${args[2]}" "$(resolve_container)" 2>&1 | redact
|
container=$(resolve_container)
|
||||||
|
docker logs --timestamps --tail "$MAX_LOG_LINES" --since "${args[2]}" "$container" 2>&1 | redact
|
||||||
;;
|
;;
|
||||||
status)
|
status)
|
||||||
[[ ${#args[@]} -eq 1 ]] || { echo "usage: status" >&2; exit 2; }
|
[[ ${#args[@]} -eq 1 ]] || { echo "usage: status" >&2; exit 2; }
|
||||||
|
|
@ -63,7 +64,13 @@ case "${args[0]}" in
|
||||||
[[ "$entry" == CHART_AUTH_TOKEN=* ]] && token=${entry#CHART_AUTH_TOKEN=}
|
[[ "$entry" == CHART_AUTH_TOKEN=* ]] && token=${entry#CHART_AUTH_TOKEN=}
|
||||||
done < <(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "$container")
|
done < <(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "$container")
|
||||||
if [[ -n "$token" ]]; then
|
if [[ -n "$token" ]]; then
|
||||||
curl -fsS -H "X-Chart-Token: $token" "$CHART_PUBLIC_URL/api/status" | redact
|
[[ "$token" != *$'\n'* && "$token" != *$'\r'* ]] || {
|
||||||
|
echo "invalid CHART_AUTH_TOKEN" >&2; exit 4;
|
||||||
|
}
|
||||||
|
escaped_token=${token//\\/\\\\}
|
||||||
|
escaped_token=${escaped_token//\"/\\\"}
|
||||||
|
printf 'url = "%s/api/status"\nheader = "X-Chart-Token: %s"\nfail\nsilent\nshow-error\n' \
|
||||||
|
"$CHART_PUBLIC_URL" "$escaped_token" | curl --config - | redact
|
||||||
else
|
else
|
||||||
echo "CHART_AUTH_TOKEN is unavailable" >&2
|
echo "CHART_AUTH_TOKEN is unavailable" >&2
|
||||||
exit 4
|
exit 4
|
||||||
|
|
@ -71,21 +78,24 @@ case "${args[0]}" in
|
||||||
;;
|
;;
|
||||||
container-state)
|
container-state)
|
||||||
[[ ${#args[@]} -eq 1 ]] || { echo "usage: container-state" >&2; exit 2; }
|
[[ ${#args[@]} -eq 1 ]] || { echo "usage: container-state" >&2; exit 2; }
|
||||||
|
container=$(resolve_container)
|
||||||
docker inspect --format \
|
docker inspect --format \
|
||||||
'name={{.Name}} image={{.Config.Image}} status={{.State.Status}} started={{.State.StartedAt}} restarts={{.RestartCount}} oom={{.State.OOMKilled}} exit={{.State.ExitCode}}' \
|
'name={{.Name}} image={{.Config.Image}} status={{.State.Status}} started={{.State.StartedAt}} restarts={{.RestartCount}} oom={{.State.OOMKilled}} exit={{.State.ExitCode}}' \
|
||||||
"$(resolve_container)"
|
"$container"
|
||||||
;;
|
;;
|
||||||
recent-deploy)
|
recent-deploy)
|
||||||
[[ ${#args[@]} -eq 1 ]] || { echo "usage: recent-deploy" >&2; exit 2; }
|
[[ ${#args[@]} -eq 1 ]] || { echo "usage: recent-deploy" >&2; exit 2; }
|
||||||
curl -fsS "$CHART_PUBLIC_URL/api/version"
|
curl -fsS "$CHART_PUBLIC_URL/api/version"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
docker inspect --format 'container_started={{.State.StartedAt}} image={{.Config.Image}}' "$(resolve_container)"
|
container=$(resolve_container)
|
||||||
|
docker inspect --format 'container_started={{.State.StartedAt}} image={{.Config.Image}}' "$container"
|
||||||
;;
|
;;
|
||||||
capture-read)
|
capture-read)
|
||||||
[[ ${#args[@]} -eq 2 ]] && valid_capture_id "${args[1]}" || {
|
[[ ${#args[@]} -eq 2 ]] && valid_capture_id "${args[1]}" || {
|
||||||
echo "usage: capture-read c-XXXXXXXXXXXX" >&2; exit 2;
|
echo "usage: capture-read c-XXXXXXXXXXXX" >&2; exit 2;
|
||||||
}
|
}
|
||||||
docker exec "$(resolve_container)" base64 "/tmp/chart-captures/${args[1]}.png"
|
container=$(resolve_container)
|
||||||
|
docker exec "$container" base64 "/tmp/chart-captures/${args[1]}.png"
|
||||||
;;
|
;;
|
||||||
capture-delete)
|
capture-delete)
|
||||||
[[ ${#args[@]} -eq 2 ]] && valid_capture_id "${args[1]}" || {
|
[[ ${#args[@]} -eq 2 ]] && valid_capture_id "${args[1]}" || {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue