fix(bootstrap): [smtp] carry stops hoarding the auth_source comment block (#50)
This commit is contained in:
2 files changed
+97
-14
No files matched your search
+20
-14
@@ -2087,24 +2087,30 @@ EOF
|
|||||||
# family as the root_domain loss fixed in ecbeb20 -- generated file, hand-set
|
# family as the root_domain loss fixed in ecbeb20 -- generated file, hand-set
|
||||||
# value, no carry-forward.
|
# value, no carry-forward.
|
||||||
#
|
#
|
||||||
# Cached on first call because write_felis_toml clobbers felis.host.toml before
|
# The carry is the section's header and key lines only. Printing every line up to
|
||||||
# it is called again for felis.pod.toml: by then the file this would read from
|
# the next section header hoarded the generated [[auth_source]] comment block
|
||||||
# no longer has the block. The pod toml is the fallback for exactly that window.
|
# that sits below [smtp] into this carry: each re-run then re-emitted the hoard
|
||||||
|
# plus a fresh template copy, growing both config files by one comment block per
|
||||||
|
# run (audit #50). The extraction is idempotent, which is also why re-reading the
|
||||||
|
# freshly rewritten host file on the pod pass is safe. The pod toml remains the
|
||||||
|
# fallback for a host file with no [smtp] section at all.
|
||||||
persisted_smtp_block() {
|
persisted_smtp_block() {
|
||||||
if [ -z "${SMTP_BLOCK_CACHED:-}" ]; then
|
local f out
|
||||||
SMTP_BLOCK_CACHED=1
|
|
||||||
SMTP_BLOCK=""
|
|
||||||
local f
|
|
||||||
for f in "${STATE_DIR}/felis.host.toml" "${STATE_DIR}/felis.pod.toml"; do
|
for f in "${STATE_DIR}/felis.host.toml" "${STATE_DIR}/felis.pod.toml"; do
|
||||||
[ -r "$f" ] || continue
|
[ -r "$f" ] || continue
|
||||||
# Print from [smtp] up to (not including) the next section header.
|
out="$(awk '
|
||||||
SMTP_BLOCK="$(awk '/^[[:space:]]*\[smtp\]/ { f=1 }
|
/^[[:space:]]*\[/ {
|
||||||
f && /^[[:space:]]*\[/ && !/^[[:space:]]*\[smtp\]/ { exit }
|
if (insmtp) exit
|
||||||
f { print }' "$f")"
|
insmtp = ($0 ~ /^[[:space:]]*\[smtp\][[:space:]]*$/)
|
||||||
[ -n "$SMTP_BLOCK" ] && break
|
if (insmtp) print
|
||||||
|
next
|
||||||
|
}
|
||||||
|
insmtp && /^[[:space:]]*("[A-Za-z_][A-Za-z0-9_]*"|[A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=/ { print }
|
||||||
|
' "$f")"
|
||||||
|
[ -n "$out" ] || continue
|
||||||
|
printf '%s' "$out"
|
||||||
|
return 0
|
||||||
done
|
done
|
||||||
fi
|
|
||||||
printf '%s' "$SMTP_BLOCK"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# persisted_auth_source_blocks echoes the [[auth_source]] tables an earlier run left
|
# persisted_auth_source_blocks echoes the [[auth_source]] tables an earlier run left
|
||||||
|
|||||||
@@ -192,6 +192,83 @@ for hdr in '[[ auth_source ]]' '[["auth_source"]]' "[['auth_source']]"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# --- [smtp] carry-forward does not hoard the auth_source comment block -------------------
|
||||||
|
# persisted_smtp_block used to print every line between [smtp] and the next section
|
||||||
|
# header -- which includes the generated Yggdrasil comment block that sits above
|
||||||
|
# [[auth_source]]. Each re-run re-emitted that hoard plus a fresh template copy, so both
|
||||||
|
# config files grew by one comment block per run (audit #50). The carry must be the
|
||||||
|
# section's header and keys only, and must be byte-stable when written back.
|
||||||
|
|
||||||
|
sblock="$(awk '/^persisted_smtp_block\(\) \{/,/^}/' "$BS")"
|
||||||
|
[ -n "$sblock" ] || { echo "FAIL: no persisted_smtp_block found in $BS"; exit 1; }
|
||||||
|
[ "$(printf '%s\n' "$sblock" | wc -l)" -lt 40 ] \
|
||||||
|
|| { echo "FAIL: the extracted block is not the function -- did its closing brace move?"; exit 1; }
|
||||||
|
|
||||||
|
sfn="$(mktemp)"
|
||||||
|
printf '%s\n' "$sblock" > "$sfn"
|
||||||
|
smtp_dir="$(mktemp -d)"
|
||||||
|
trap 'rm -f "$jar" "$sfn"; rm -rf "$vdir" "$sdir" "$smtp_dir"' EXIT
|
||||||
|
|
||||||
|
run_smtp() { # state-dir
|
||||||
|
STATE_DIR="$1" SBLOCK_FILE="$sfn" bash -c '. "$SBLOCK_FILE"; persisted_smtp_block'
|
||||||
|
}
|
||||||
|
|
||||||
|
cat > "$smtp_dir/felis.host.toml" <<'TOML'
|
||||||
|
[server]
|
||||||
|
listen = "0.0.0.0:8080"
|
||||||
|
|
||||||
|
[smtp]
|
||||||
|
host = "mail.example"
|
||||||
|
port = 587
|
||||||
|
from = "[email protected]"
|
||||||
|
username = "relay-user"
|
||||||
|
password_ref = "smtp-password"
|
||||||
|
|
||||||
|
# Third-party Yggdrasil sources federated by the hasJoined multiplexer. Mojang is
|
||||||
|
# always the code-owned identity anchor (premium-first), prepended in Go; sources here
|
||||||
|
# append as namespace-rewritten guests. A fresh install federates LittleSkin. Edit the
|
||||||
|
# list in /etc/felis/felis.host.toml and rerun the installer; re-runs keep it as it
|
||||||
|
# is, and with no [[auth_source]] at all the server is Mojang-only.
|
||||||
|
|
||||||
|
[[auth_source]]
|
||||||
|
tag = "littleskin"
|
||||||
|
prefix = "LS"
|
||||||
|
url = "https://littleskin.cn/api/yggdrasil/sessionserver/session/minecraft/hasJoined"
|
||||||
|
TOML
|
||||||
|
|
||||||
|
out="$(run_smtp "$smtp_dir")"
|
||||||
|
expect "a configured [smtp] relay is carried" 'host = "mail.example"' "$out"
|
||||||
|
expect "its port survives the carry" 'port = 587' "$out"
|
||||||
|
expect "its credentials reference survives" 'password_ref = "smtp-password"' "$out"
|
||||||
|
case "$out" in
|
||||||
|
*"#"*)
|
||||||
|
echo "FAIL: the carry hoards comment lines:"; printf '%s\n' "$out"; fails=$((fails + 1)) ;;
|
||||||
|
*) echo "PASS the carry is header and keys only -- no comment hoard" ;;
|
||||||
|
esac
|
||||||
|
case "$out" in
|
||||||
|
*"[["*)
|
||||||
|
echo "FAIL: the carry ran into the next section:"; printf '%s\n' "$out"; fails=$((fails + 1)) ;;
|
||||||
|
*) echo "PASS the carry stops at the next section header" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Write the carry back the way write_felis_toml does (carry + one fresh template block +
|
||||||
|
# the tables) and extract again: a second re-run must add nothing.
|
||||||
|
{
|
||||||
|
printf '%s\n' "$out"
|
||||||
|
printf '\n%s\n' '# Third-party Yggdrasil sources federated by the hasJoined multiplexer. Mojang is'
|
||||||
|
printf '%s\n' '[[auth_source]]' ' tag = "littleskin"' ' prefix = "LS"' \
|
||||||
|
' url = "https://littleskin.cn/api/yggdrasil/sessionserver/session/minecraft/hasJoined"'
|
||||||
|
} > "$smtp_dir/felis.host.toml"
|
||||||
|
out2="$(run_smtp "$smtp_dir")"
|
||||||
|
printf '%s\n' "$out" > "$smtp_dir/first"
|
||||||
|
printf '%s\n' "$out2" > "$smtp_dir/second"
|
||||||
|
if cmp -s "$smtp_dir/first" "$smtp_dir/second"; then
|
||||||
|
echo "PASS a carried-forward [smtp] converges (a second re-run adds nothing)"
|
||||||
|
else
|
||||||
|
echo "FAIL: carrying [smtp] is not idempotent:"; diff "$smtp_dir/first" "$smtp_dir/second" | head
|
||||||
|
fails=$((fails + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
# --- write_nano_config leaves the unit able to read its config ---------------------------
|
# --- 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
|
# felis-nano runs as a DynamicUser, so the directory must be searchable by others under a
|
||||||
# hardened umask too, including one an older installer left at 0750 -- but the full
|
# hardened umask too, including one an older installer left at 0750 -- but the full
|
||||||
|
|||||||
Reference in new issue
Block a user