fix(bootstrap): secrets.env、offsite.env、felis.toml 改为临时文件+fsync+rename 原子写入

This commit is contained in:
Lemon-miaow committed 2026-09-27 09:47:26 +08:00
1 parent c1cdef0d6a
commit 339224bdc2
2 files changed
+104 -14

No files matched your search

+23 -12
View File
@@ -705,6 +705,24 @@ secret_key_to_file() {
mv -f -- "$tmp" "$path"
}
# write_file_atomic path mode < content: path ends up holding all of content, or is
# left as it was. A crash or a full disk halfway through a plain `cat >` leaves a
# truncated file that the next run takes as the truth: a secrets.env cut short mints
# passwords the database does not know, an offsite.env cut short a key that cannot
# read the bucket. The temp file sits beside path, so mv is a rename on one
# filesystem; mktemp makes it 0600 before anything is in it, it is on the disk
# before the rename, and the directory is synced after it, so the new name survives
# a power cut too.
write_file_atomic() {
local path="$1" mode="$2" tmp
tmp="$(mktemp "${path}.XXXXXX")" || die "could not create a temporary file beside ${path}"
remember_temp "$tmp"
{ cat > "$tmp" && chmod "$mode" "$tmp" && sync -- "$tmp"; } \
|| die "could not write ${path}; it is left as it was"
mv -f -- "$tmp" "$path" || die "could not replace ${path}; it is left as it was"
sync -- "$(dirname -- "$path")" 2>/dev/null || true
}
# apply_setup_credential_secrets applies the Secrets behind `felis setup`'s email and
# uploads-bucket screens from their host copies: felis-smtp in the control namespace and
# in the workload one (the reaper's warning mails read that copy), felis-uploads-s3 in the
@@ -4155,9 +4173,7 @@ load_or_make_secrets() {
REGISTRY_PLATFORM_TOKEN="${REGISTRY_PLATFORM_TOKEN:-$(openssl rand -hex 32)}"
REGISTRY_BUILD_TOKEN="${REGISTRY_BUILD_TOKEN:-$(openssl rand -hex 32)}"
REGISTRY_PRUNE_TOKEN="${REGISTRY_PRUNE_TOKEN:-$(openssl rand -hex 32)}"
(
umask 077
cat > "$SECRETS_ENV" <<EOF
write_file_atomic "$SECRETS_ENV" 0600 <<EOF
DB_PASSWORD=${DB_PASSWORD}
SERVICE_TOKEN=${SERVICE_TOKEN}
LIMBO_TOKEN=${LIMBO_TOKEN}
@@ -4169,8 +4185,6 @@ REGISTRY_PLATFORM_TOKEN=${REGISTRY_PLATFORM_TOKEN}
REGISTRY_BUILD_TOKEN=${REGISTRY_BUILD_TOKEN}
REGISTRY_PRUNE_TOKEN=${REGISTRY_PRUNE_TOKEN}
EOF
)
chmod 0600 "$SECRETS_ENV"
}
ensure_panel_tls_cert() {
@@ -4432,7 +4446,8 @@ offsite_enabled() {
# write_felis_toml target host:port [namespace/deployment]: the deployment is the
# database's pod, where `felis db` runs pg_dump, pg_restore and psql (the host has no
# PostgreSQL client); only the host copy names it.
# PostgreSQL client); only the host copy names it. The file is 0600: its url holds the
# database password.
write_felis_toml() {
local target="$1" db_addr="$2" deployment="${3:-}" deployment_line="" smtp_block auth_body auth_source_blocks registry_block archive_block offsite_section
if [ -n "$deployment" ]; then
@@ -4467,7 +4482,7 @@ deployment = \"${deployment}\""
if [ -n "$offsite_section" ]; then
offsite_section="${offsite_section}"$'\n\n' # keep a blank line before the next section
fi
cat > "$target" <<EOF
write_file_atomic "$target" 0600 <<EOF
# Generated by deploy/bootstrap.sh; rerun the installer to regenerate. Hand edits are
# overwritten, except [auth], [smtp], [[auth_source]], [offsite], and the operator-owned
# [registry] / [archive] overrides, which carry forward. Move the install to another
@@ -4993,9 +5008,7 @@ configure_offsite() {
FELIS_OFFSITE_KEY="$(openssl rand -base64 32)"
OFFSITE_KEY_NEW=1
fi
(
umask 077
cat > "$OFFSITE_ENV" <<EOF
write_file_atomic "$OFFSITE_ENV" 0600 <<EOF
# The off-site copy's secrets (felis offsite, docs/troubleshooting.md §16). Keep a copy of
# FELIS_OFFSITE_KEY somewhere other than this machine: without it the copies in the
# bucket cannot be read, and this file goes with the machine.
@@ -5003,8 +5016,6 @@ FELIS_OFFSITE_ACCESS_KEY='${FELIS_OFFSITE_ACCESS_KEY}'
FELIS_OFFSITE_SECRET_KEY='${FELIS_OFFSITE_SECRET_KEY}'
FELIS_OFFSITE_KEY='${FELIS_OFFSITE_KEY}'
EOF
)
chmod 0600 "$OFFSITE_ENV"
ok "off-site copy: secrets in ${OFFSITE_ENV}"
}