Skip to content

Commit

Permalink
fix(permissions): chalk runs for users without home directory
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
miki725 committed Dec 21, 2023
1 parent 87ffc5e commit 01e2b0d
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 23 deletions.
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
22 changes: 19 additions & 3 deletions src/confload.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@ proc findOptionalConf(state: ConfigState): Option[(string, FileStream)] =
path = unpack[seq[string]](state.attrLookup("config_path").get())
filename = unpack[string](state.attrLookup("config_filename").get())
for dir in path:
let fname = resolvePath(dir.joinPath(filename))
var fname = ""
try:
fname = resolvePath(dir.joinPath(filename))
except:
# resolvePath can fail in some cases such as ~ might not resolve
# if uid does not have home folder
trace(filename & ": Cannot resolve configuration file path.")
continue
trace("Looking for config file at: " & fname)
if fname.fileExists():
info(fname & ": Found config file")
Expand All @@ -107,8 +114,17 @@ proc loadLocalStructs*(state: ConfigState) =
chalkConfig = state.attrs.loadChalkConfig()
if chalkConfig.color.isSome(): setShowColor(chalkConfig.color.get())
setLogLevel(chalkConfig.logLevel)
for i in 0 ..< len(chalkConfig.configPath):
chalkConfig.configPath[i] = chalkConfig.configPath[i].resolvePath()
var configPath: seq[string] = @[]
for path in chalkConfig.configPath:
try:
configPath.add(path.resolvePath())
except:
# resolvePath can fail in some cases such as ~ might not resolve
# if uid does not have home folder
# no log as this function is called multiple times
# and any logs are very verbose
continue
chalkConfig.configPath = configPath
var c4errLevel = if chalkConfig.con4mPinpoint: c4vShowLoc else: c4vBasic

if chalkConfig.chalkDebug:
Expand Down
21 changes: 10 additions & 11 deletions src/selfextract.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,17 @@ proc getSelfExtraction*(): Option[ChalkObj] =
# have a codec for this type of executable, avoid dupe errors.
once:
var
myPath = resolvePath(getMyAppPath())
myPath = getMyAppPath()
cmd = getCommandName()

try:
myPath = myPath.resolvePath()
except:
# should not happen as getMyAppPath should return absolute path
# however resolvePath can fail in some cases such as when
# path contains ~ but uid does not have home directory
discard

setCommandName("extract")

# This can stay here, but won't show if the log level is set in the
Expand Down Expand Up @@ -61,16 +69,7 @@ proc getSelfExtraction*(): Option[ChalkObj] =
# of lettus to enjoy ;D
# If not, we immediately exit with hopefully useful error message
# :fingerscrossed:
var canOpen = false
try:
let stream = newFileStream(myPath)
if stream != nil:
canOpen = true
stream.close()
except:
dumpExOnDebug()
error(getCurrentExceptionMsg())
if not canOpen:
if not canOpenFile(myPath):
cantLoad("Chalk is unable to read self-config. " &
"Ensure chalk has both read and execute permissions. " &
"To add permissions run: 'chmod +rx " & myPath & "'\n")
Expand Down
3 changes: 2 additions & 1 deletion src/sinks.nim
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ template formatIo(cfg: SinkConfig, t: Topic, err: string, msg: string): string =

case cfg.mySink.name
of "rotating_log", "file":
line &= cfg.params["actual_file"] & ": "
if "actual_file" in cfg.params:
line &= cfg.params["actual_file"] & ": "
else:
discard

Expand Down
40 changes: 34 additions & 6 deletions src/util.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ proc reportTmpFileExitState*(files, dirs, errs: seq[string]) =
1000000000) &
" seconds"

proc canOpenFile*(path: string, mode: FileMode = FileMode.fmRead): bool =
var canOpen = false
try:
let stream = openFileStream(path, mode = mode)
if stream != nil:
canOpen = true
stream.close()
except:
dumpExOnDebug()
error(getCurrentExceptionMsg())
finally:
if mode != FileMode.fmRead:
try:
discard tryRemoveFile(path)
except:
discard
return canOpen

proc setupManagedTemp*() =
let customTmpDirOpt = chalkConfig.getDefaultTmpDir()

Expand All @@ -119,14 +137,19 @@ proc setupManagedTemp*() =
discard existsOrCreateDir(getEnv("TMPDIR"))

if chalkConfig.getChalkDebug():
info("Debug is on; temp files / dirs will be moved, not deleted.")
setManagedTmpCopyLocation(resolvePath("chalk-tmp"))
let
tmpPath = resolvePath("chalk-tmp")
tmpCheck = resolvePath(".chalk-tmp-check")
if canOpenFile(tmpCheck, mode = FileMode.fmWrite):
info("Debug is on; temp files / dirs will be moved to " & tmpPath & ", not deleted.")
setManagedTmpCopyLocation(tmpPath)
else:
warn("Debug is on however chalk is unable to move temp files to " & tmpPath)

setManagedTmpExitCallback(reportTmpFileExitState)
setDefaultTmpFilePrefix(tmpFilePrefix)
setDefaultTmpFileSuffix(tmpFileSuffix)


when hostOs == "macosx":
const staticScriptLoc = "autocomplete/mac.bash"
else:
Expand Down Expand Up @@ -230,10 +253,15 @@ proc validateMetadata*(obj: ChalkObj): ValidateResult {.importc.}
proc autocompleteFileCheck*() =
if isatty(0) == 0 or chalkConfig.getInstallCompletionScript() == false:
return
let
dst = resolvePath(autoCompleteLoc)
alreadyExists = fileExists(dst)

var dst = ""
try:
dst = resolvePath(autoCompleteLoc)
except:
# resolvePath can fail on ~ when uid doesnt have home dir
return

let alreadyExists = fileExists(dst)
if alreadyExists:
var invalidMark = true

Expand Down
11 changes: 11 additions & 0 deletions tests/test_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,14 @@ def test_extract(chalk: Chalk, random_hex: str):
virtual=False,
chalk_action="extract",
)


def test_docker_diff_user(chalk_default: Chalk):
assert Docker.run(
"alpine",
entrypoint="/chalk",
params=["version"],
volumes={chalk_default.binary: "/chalk"},
cwd=chalk_default.binary.parent,
user="1000:1000",
)
9 changes: 9 additions & 0 deletions tests/utils/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ def run(
tty: bool = True,
check: bool = True,
attach: bool = True,
user: Optional[str] = None,
volumes: Optional[dict[Path, Path | str]] = None,
cwd: Optional[Path] = None,
):
cmd = ["docker", "create"]
if name:
Expand All @@ -161,6 +164,12 @@ def run(
cmd += ["--entrypoint", entrypoint]
if tty:
cmd += ["-t"]
if user:
cmd += ["-u", user]
for host, container in (volumes or {}).items():
cmd += ["-v", f"{host}:{container}"]
if cwd:
cmd += ["-w", str(cwd)]
cmd += [image]
cmd += params or []
container_id = run(cmd).text
Expand Down

0 comments on commit 01e2b0d

Please sign in to comment.