fix(bootstrap): create the nano config dir world-searchable

felis-nano runs as a systemd DynamicUser, so it can read
/etc/felis/felis.toml only if others may search /etc/felis.
write_nano_config made the directory with a bare mkdir -p, which takes
its mode from root's umask. On a host hardened to umask 027 that is
0750: nano exits on "permission denied", the unit restarts every five
seconds, and no login gets through.

A missing directory is now created 0755 explicitly. An existing one
keeps its mode, because the full install sets it to 0700 to protect its
secrets and widening that from the nano path would expose them. A nano
unit locked out that way is left for the install to report.

The harness runs the extracted function under umask 027 and checks both
cases. Reverting to the bare mkdir fails the first; an unconditional
chmod 0755 fails the second. The mode checks skip on filesystems that
ignore chmod, such as Git Bash on NTFS.
This commit is contained in:
flyemoji committed 2026-09-22 13:04:34 +09:00
1 parent 1dd62a9bdc
commit 26f685be0e
2 files changed
+32 -1

No files matched your search

+5 -1
View File
@@ -2300,7 +2300,11 @@ acquire_nano_binary() {
write_nano_config() {
local target="${STATE_DIR}/felis.toml"
mkdir -p "$STATE_DIR"
# The unit is a DynamicUser, so it can read felis.toml only if it can search this
# directory. The mode is explicit because a hardened root umask (027) would leave it 0750.
# An existing directory keeps its mode: the full install locks it to 0700 for its secrets,
# and install_nano_service reports that lockout rather than this widening it.
[ -d "$STATE_DIR" ] || mkdir -p -m 0755 "$STATE_DIR"
if [ -e "$target" ]; then
ok "config already present at ${target}; leaving it (edit it to add [[auth_source]] roots)"
return 0
+27
View File
@@ -179,6 +179,33 @@ out="$(run_carry)"
expect "both encoder-written tables are carried (first)" ' tag = "littleskin"' "$out"
expect "both encoder-written tables are carried (second)" ' tag = "guild"' "$out"
# --- write_nano_config leaves the unit able to read its config ---------------------------
# felis-nano runs as a DynamicUser, so the directory must be searchable by others under a
# hardened umask too -- but an existing one, which the full install locks to 0700 for its
# secrets, must not be widened.
wblock="$(awk '/^write_nano_config\(\) \{/,/^}/' "$BS")"
[ -n "$wblock" ] || { echo "FAIL: no write_nano_config found in $BS"; exit 1; }
[ "$(printf '%s\n' "$wblock" | wc -l)" -lt 40 ] \
|| { echo "FAIL: the extracted block is not the function -- did its closing brace move?"; exit 1; }
run_nano_config() { # state-dir
STATE_DIR="$1" bash -c 'umask 027
ok() { printf "OK: %s\n" "$*"; }
'"$wblock"'
write_nano_config'
}
mkdir "$sdir/probe" && chmod 0700 "$sdir/probe"
if [ "$(stat -c %a "$sdir/probe")" = 700 ]; then
run_nano_config "$sdir/nano" >/dev/null
expect "a fresh config dir is searchable under umask 027" 755 "$(stat -c %a "$sdir/nano")"
run_nano_config "$sdir/probe" >/dev/null
expect "an existing 0700 dir is not widened" 700 "$(stat -c %a "$sdir/probe")"
else
echo "SKIP directory modes: this filesystem ignores chmod"
fi
# ---------------------------------------------------------------------------------------
if [ "$fails" -eq 0 ]; then
echo "ALL PASS"