Unverified Commit 26f685be authored by Minseong Choi's avatar Minseong Choi 💬
Browse files

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.
parent 1dd62a9b
Loading
Loading
Loading
Loading
+5 −1
Changes for deploy/bootstrap.sh: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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 −0
Changes for deploy/bootstrap_test.sh: 27 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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"