fix: force-stop service account during purge
This commit is contained in:
+76
-7
@@ -67,8 +67,61 @@ related_pids() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
managed_service_account() {
|
||||||
|
local entry home shell
|
||||||
|
entry=$(getent passwd "$SERVICE_USER" 2>/dev/null || true)
|
||||||
|
[ -n "$entry" ] || return 1
|
||||||
|
home=$(printf '%s\n' "$entry" | awk -F: '{print $6}')
|
||||||
|
shell=$(printf '%s\n' "$entry" | awk -F: '{print $7}')
|
||||||
|
case "$home:$shell" in
|
||||||
|
"$STATE_ROOT:"*/nologin|"$STATE_ROOT:"*/false) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
service_user_pids() {
|
||||||
|
local uid=$1 proc pid owner_uid
|
||||||
|
for proc in /proc/[0-9]*; do
|
||||||
|
[ -d "$proc" ] || continue
|
||||||
|
pid=${proc##*/}
|
||||||
|
owner_uid=$(stat -c '%u' "$proc" 2>/dev/null || true)
|
||||||
|
[ "$owner_uid" = "$uid" ] && printf '%s\n' "$pid"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate_uid_processes() {
|
||||||
|
local uid=$1 deadline
|
||||||
|
local -a pids=()
|
||||||
|
mapfile -t pids < <(service_user_pids "$uid")
|
||||||
|
if [ "${#pids[@]}" -gt 0 ]; then
|
||||||
|
log "Stopping processes owned by the dedicated $SERVICE_USER account: ${pids[*]}"
|
||||||
|
kill -TERM "${pids[@]}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
deadline=$((SECONDS + 5))
|
||||||
|
while [ "$SECONDS" -lt "$deadline" ]; do
|
||||||
|
mapfile -t pids < <(service_user_pids "$uid")
|
||||||
|
[ "${#pids[@]}" -gt 0 ] || return 0
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
kill -KILL "${pids[@]}" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
mapfile -t pids < <(service_user_pids "$uid")
|
||||||
|
[ "${#pids[@]}" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
all_pids_owned_by_service_user() {
|
||||||
|
local service_uid pid owner_uid
|
||||||
|
managed_service_account || return 1
|
||||||
|
service_uid=$(id -u "$SERVICE_USER")
|
||||||
|
for pid in "$@"; do
|
||||||
|
[ -d "/proc/$pid" ] || continue
|
||||||
|
owner_uid=$(stat -c '%u' "/proc/$pid" 2>/dev/null || true)
|
||||||
|
[ "$owner_uid" = "$service_uid" ] || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
stop_managed_processes() {
|
stop_managed_processes() {
|
||||||
local deadline
|
local allow_service_restart=${1:-false} deadline
|
||||||
local -a pids=()
|
local -a pids=()
|
||||||
mapfile -t pids < <(related_pids)
|
mapfile -t pids < <(related_pids)
|
||||||
if [ "${#pids[@]}" -gt 0 ]; then
|
if [ "${#pids[@]}" -gt 0 ]; then
|
||||||
@@ -84,8 +137,12 @@ stop_managed_processes() {
|
|||||||
kill -KILL "${pids[@]}" 2>/dev/null || true
|
kill -KILL "${pids[@]}" 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
mapfile -t pids < <(related_pids)
|
mapfile -t pids < <(related_pids)
|
||||||
[ "${#pids[@]}" -eq 0 ] \
|
[ "${#pids[@]}" -eq 0 ] && return 0
|
||||||
|| die "A process manager is restarting Kaidi; remove the Kaidi project from Baota and run this command again"
|
if [ "$allow_service_restart" = true ] && all_pids_owned_by_service_user "${pids[@]}"; then
|
||||||
|
log "A process manager restarted the dedicated $SERVICE_USER account; forced account cleanup will stop it"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
die "A process manager is restarting Kaidi; remove the Kaidi project from Baota and run this command again"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_recovery_backup() {
|
create_recovery_backup() {
|
||||||
@@ -120,6 +177,10 @@ create_recovery_backup() {
|
|||||||
|
|
||||||
remove_systemd_units() {
|
remove_systemd_units() {
|
||||||
local unit
|
local unit
|
||||||
|
rm -rf \
|
||||||
|
/etc/systemd/system/kaidi-finance.service.d \
|
||||||
|
/etc/systemd/system/kaidi-update.service.d \
|
||||||
|
/etc/systemd/system/kaidi-update.path.d
|
||||||
rm -f \
|
rm -f \
|
||||||
/etc/systemd/system/kaidi-finance.service \
|
/etc/systemd/system/kaidi-finance.service \
|
||||||
/etc/systemd/system/kaidi-update.service \
|
/etc/systemd/system/kaidi-update.service \
|
||||||
@@ -149,15 +210,23 @@ remove_download_archives() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove_service_identity() {
|
remove_service_identity() {
|
||||||
local entry home shell group_entry gid members primary_users
|
local entry home shell service_uid group_entry gid members primary_users
|
||||||
entry=$(getent passwd "$SERVICE_USER" 2>/dev/null || true)
|
entry=$(getent passwd "$SERVICE_USER" 2>/dev/null || true)
|
||||||
if [ -n "$entry" ]; then
|
if [ -n "$entry" ]; then
|
||||||
home=$(printf '%s\n' "$entry" | awk -F: '{print $6}')
|
home=$(printf '%s\n' "$entry" | awk -F: '{print $6}')
|
||||||
shell=$(printf '%s\n' "$entry" | awk -F: '{print $7}')
|
shell=$(printf '%s\n' "$entry" | awk -F: '{print $7}')
|
||||||
case "$home:$shell" in
|
case "$home:$shell" in
|
||||||
"$STATE_ROOT:"*/nologin|"$STATE_ROOT:"*/false)
|
"$STATE_ROOT:"*/nologin|"$STATE_ROOT:"*/false)
|
||||||
userdel "$SERVICE_USER" \
|
service_uid=$(id -u "$SERVICE_USER")
|
||||||
|
terminate_uid_processes "$service_uid" || true
|
||||||
|
if ! userdel --force "$SERVICE_USER"; then
|
||||||
|
terminate_uid_processes "$service_uid" || true
|
||||||
|
userdel --force "$SERVICE_USER" \
|
||||||
|| die "Failed to remove the dedicated $SERVICE_USER service account"
|
|| die "Failed to remove the dedicated $SERVICE_USER service account"
|
||||||
|
fi
|
||||||
|
terminate_uid_processes "$service_uid" \
|
||||||
|
|| die "Processes owned by the removed $SERVICE_USER account are still running"
|
||||||
|
rm -rf "/run/user/$service_uid"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
log "Keeping pre-existing user $SERVICE_USER because its home or shell is not Kaidi-managed"
|
log "Keeping pre-existing user $SERVICE_USER because its home or shell is not Kaidi-managed"
|
||||||
@@ -182,13 +251,13 @@ main() {
|
|||||||
validate_inputs
|
validate_inputs
|
||||||
log "External MySQL data and reverse-proxy configuration will not be modified"
|
log "External MySQL data and reverse-proxy configuration will not be modified"
|
||||||
stop_systemd_units
|
stop_systemd_units
|
||||||
stop_managed_processes
|
stop_managed_processes true
|
||||||
create_recovery_backup
|
create_recovery_backup
|
||||||
remove_systemd_units
|
remove_systemd_units
|
||||||
remove_managed_paths
|
remove_managed_paths
|
||||||
remove_download_archives
|
remove_download_archives
|
||||||
remove_service_identity
|
remove_service_identity
|
||||||
stop_managed_processes
|
stop_managed_processes false
|
||||||
log "Local Kaidi installation state has been removed"
|
log "Local Kaidi installation state has been removed"
|
||||||
if [ -n "$BACKUP_ARCHIVE" ]; then
|
if [ -n "$BACKUP_ARCHIVE" ]; then
|
||||||
log "Recovery backup: $BACKUP_ARCHIVE"
|
log "Recovery backup: $BACKUP_ARCHIVE"
|
||||||
|
|||||||
@@ -83,5 +83,13 @@ grep -Fq 'KAIDI_PURGE_CONFIRM=$REQUIRED_CONFIRMATION' "$ROOT/deploy/purge.sh" \
|
|||||||
|| fail 'destructive removal no longer requires explicit confirmation'
|
|| fail 'destructive removal no longer requires explicit confirmation'
|
||||||
grep -Fq 'External MySQL data and reverse-proxy configuration will not be modified' "$ROOT/deploy/purge.sh" \
|
grep -Fq 'External MySQL data and reverse-proxy configuration will not be modified' "$ROOT/deploy/purge.sh" \
|
||||||
|| fail 'purge boundary is no longer explicit'
|
|| fail 'purge boundary is no longer explicit'
|
||||||
|
# shellcheck disable=SC2016 # Match literal service-account cleanup calls.
|
||||||
|
grep -Fq 'terminate_uid_processes "$service_uid"' "$ROOT/deploy/purge.sh" \
|
||||||
|
|| fail 'dedicated service-account processes are not terminated before account removal'
|
||||||
|
# shellcheck disable=SC2016 # Match literal forced account removal.
|
||||||
|
grep -Fq 'userdel --force "$SERVICE_USER"' "$ROOT/deploy/purge.sh" \
|
||||||
|
|| fail 'dedicated service-account removal is not resilient to a restarting panel process'
|
||||||
|
grep -Fq '/etc/systemd/system/kaidi-update.service.d' "$ROOT/deploy/purge.sh" \
|
||||||
|
|| fail 'updater systemd drop-ins are not removed'
|
||||||
|
|
||||||
printf 'Local purge and recovery fixture passed\n'
|
printf 'Local purge and recovery fixture passed\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user