This commit is contained in:
@@ -5,7 +5,7 @@ umask 077
|
||||
|
||||
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
INSTALLER="$ROOT/deploy/install.sh"
|
||||
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-8a75ee99a1c2f426c11ea18c8ef65f4f4ac3f8d52321c358f3ff645baa6ad99c}
|
||||
INSTALLER_SHA256=${KAIDI_INSTALLER_SHA256:-bc9001197af843dc323907a98023064ccc4f184e000543d2086b8a6f8161dbc7}
|
||||
RELEASE_API_URL=${KAIDI_RELEASE_API_URL:-https://git.awaioi.com/api/v1/repos/ERP-Team/kaidi/releases/latest}
|
||||
PUBLIC_KEY_SHA256=${KAIDI_RELEASE_PUBLIC_KEY_SHA256:-807c6aec1dc3f7ce494db16aa9d763c66f292033c38f328afd0390d2715a8cd9}
|
||||
TOKEN_FILE=${KAIDI_RELEASE_TOKEN_FILE:-}
|
||||
|
||||
+179
-46
@@ -28,6 +28,9 @@ BACKUP_DIR=
|
||||
JAVA_STAGED_DIR=
|
||||
JAVA_PREVIOUS_DIR=
|
||||
JAVA_ACTIVATED=false
|
||||
SERVICE_USER=kaidi
|
||||
SERVICE_GROUP=kaidi
|
||||
HOST_ARCH=
|
||||
|
||||
log() { printf '[kaidi-install] %s\n' "$*"; }
|
||||
die() { printf '[kaidi-install] ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
@@ -36,14 +39,42 @@ sha256_file() {
|
||||
sha256sum "$1" | awk '{print $1}'
|
||||
}
|
||||
|
||||
normalized_host_arch() {
|
||||
local machine host_bits
|
||||
machine=$(uname -m)
|
||||
host_bits=$(getconf LONG_BIT 2>/dev/null || true)
|
||||
case "$machine" in
|
||||
x86_64|amd64)
|
||||
[ "$host_bits" = 32 ] && printf x86 || printf x86_64
|
||||
;;
|
||||
i386|i486|i586|i686) printf x86 ;;
|
||||
aarch64|arm64)
|
||||
[ "$host_bits" = 32 ] && printf arm || printf aarch64
|
||||
;;
|
||||
armv7l|armv6l) printf arm ;;
|
||||
*) die "Unsupported CPU architecture: $machine" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
preflight_host() {
|
||||
local host_bits expected_bits
|
||||
[ "$(uname -s)" = Linux ] || die "The installer only supports Linux"
|
||||
[ "$APP_ROOT" = /opt/kaidi ] && [ "$STATE_ROOT" = /var/lib/kaidi ] \
|
||||
&& [ "$UPDATE_STATE_ROOT" = /var/lib/kaidi-update ] && [ "$CONFIG_ROOT" = /etc/kaidi ] \
|
||||
|| die "Custom installation roots are not supported by the packaged systemd configuration"
|
||||
command -v systemctl >/dev/null 2>&1 || die "systemd is required"
|
||||
command -v systemd-analyze >/dev/null 2>&1 || die "systemd-analyze is required"
|
||||
command -v getconf >/dev/null 2>&1 || die "getconf is required"
|
||||
[ -d /run/systemd/system ] || die "systemd is not running as PID 1"
|
||||
[ -d /etc/systemd/system ] || die "/etc/systemd/system is missing"
|
||||
HOST_ARCH=$(normalized_host_arch)
|
||||
host_bits=$(getconf LONG_BIT 2>/dev/null || true)
|
||||
case "$HOST_ARCH" in
|
||||
x86_64|aarch64) expected_bits=64 ;;
|
||||
x86|arm) expected_bits=32 ;;
|
||||
esac
|
||||
[ "$host_bits" = "$expected_bits" ] \
|
||||
|| die "CPU architecture and userspace word size do not match: $HOST_ARCH/$host_bits-bit"
|
||||
[[ "$TRUSTED_PUBLIC_KEY_SHA256" =~ ^[0-9A-Fa-f]{64}$ ]] \
|
||||
|| die "Set KAIDI_RELEASE_PUBLIC_KEY_SHA256 to the trusted release public-key SHA-256"
|
||||
case "$REINSTALL" in
|
||||
@@ -62,28 +93,31 @@ preflight_host() {
|
||||
[ ! -e "$STATE_ROOT/setup/locked" ] \
|
||||
|| die "The setup wizard is already locked; use a normal repair reinstall"
|
||||
fi
|
||||
log "Environment verified: $(uname -sr), architecture=$HOST_ARCH, userspace=${host_bits}-bit, systemd"
|
||||
}
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || die "Run with sudo or as root"
|
||||
[ -z "$RELEASE_TOKEN" ] || [ -z "$RELEASE_TOKEN_FILE" ] \
|
||||
|| die "Set only one of KAIDI_RELEASE_TOKEN or KAIDI_RELEASE_TOKEN_FILE"
|
||||
if [ -n "$RELEASE_TOKEN_FILE" ]; then
|
||||
[ -f "$RELEASE_TOKEN_FILE" ] && [ ! -L "$RELEASE_TOKEN_FILE" ] \
|
||||
|| die "KAIDI_RELEASE_TOKEN_FILE must be a regular file"
|
||||
[ "$(wc -c < "$RELEASE_TOKEN_FILE" | tr -d '[:space:]')" -le 512 ] \
|
||||
|| die "KAIDI_RELEASE_TOKEN_FILE is too large"
|
||||
RELEASE_TOKEN=$(cat "$RELEASE_TOKEN_FILE")
|
||||
fi
|
||||
[ "$REINSTALL" = "true" ] || [ ! -e "$APP_ROOT/current" ] \
|
||||
|| die "Kaidi Finance is already installed; use the system update page"
|
||||
if [ -z "$RELEASE_API_URL" ]; then
|
||||
case "$RELEASE_BASE_URL" in
|
||||
*OWNER/REPO*) die "Set KAIDI_RELEASE_BASE_URL or KAIDI_RELEASE_API_URL to the Git release source" ;;
|
||||
esac
|
||||
fi
|
||||
[ -z "$RELEASE_TOKEN" ] || { [ "${#RELEASE_TOKEN}" -le 512 ] \
|
||||
&& ! printf '%s' "$RELEASE_TOKEN" | grep -q '[[:cntrl:]]'; } \
|
||||
|| die "KAIDI_RELEASE_TOKEN is invalid"
|
||||
validate_inputs() {
|
||||
[ "$(id -u)" -eq 0 ] || die "Run with sudo or as root"
|
||||
[ -z "$RELEASE_TOKEN" ] || [ -z "$RELEASE_TOKEN_FILE" ] \
|
||||
|| die "Set only one of KAIDI_RELEASE_TOKEN or KAIDI_RELEASE_TOKEN_FILE"
|
||||
if [ -n "$RELEASE_TOKEN_FILE" ]; then
|
||||
[ -f "$RELEASE_TOKEN_FILE" ] && [ ! -L "$RELEASE_TOKEN_FILE" ] \
|
||||
|| die "KAIDI_RELEASE_TOKEN_FILE must be a regular file"
|
||||
[ "$(wc -c < "$RELEASE_TOKEN_FILE" | tr -d '[:space:]')" -le 512 ] \
|
||||
|| die "KAIDI_RELEASE_TOKEN_FILE is too large"
|
||||
RELEASE_TOKEN=$(cat "$RELEASE_TOKEN_FILE")
|
||||
fi
|
||||
[ "$REINSTALL" = "true" ] || [ ! -e "$APP_ROOT/current" ] \
|
||||
|| die "Kaidi Finance is already installed; use the system update page"
|
||||
if [ -z "$RELEASE_API_URL" ]; then
|
||||
case "$RELEASE_BASE_URL" in
|
||||
*OWNER/REPO*) die "Set KAIDI_RELEASE_BASE_URL or KAIDI_RELEASE_API_URL to the Git release source" ;;
|
||||
esac
|
||||
fi
|
||||
[ -z "$RELEASE_TOKEN" ] || { [ "${#RELEASE_TOKEN}" -le 512 ] \
|
||||
&& ! printf '%s' "$RELEASE_TOKEN" | grep -q '[[:cntrl:]]'; } \
|
||||
|| die "KAIDI_RELEASE_TOKEN is invalid"
|
||||
}
|
||||
|
||||
download() {
|
||||
local url=$1 output=$2
|
||||
@@ -170,12 +204,26 @@ install_packages() {
|
||||
}
|
||||
|
||||
azul_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64) printf x86 ;;
|
||||
i386|i486|i586|i686) printf i686 ;;
|
||||
aarch64|arm64) printf arm_64 ;;
|
||||
armv7l|armv6l) printf arm ;;
|
||||
*) die "Unsupported CPU architecture: $(uname -m)" ;;
|
||||
case "${HOST_ARCH:-$(normalized_host_arch)}" in
|
||||
x86_64) printf x86 ;;
|
||||
x86) printf i686 ;;
|
||||
aarch64) printf arm_64 ;;
|
||||
arm) printf arm ;;
|
||||
esac
|
||||
}
|
||||
|
||||
java_arch_matches_host() {
|
||||
local java_bin=$1 java_arch host_arch
|
||||
host_arch=${HOST_ARCH:-$(normalized_host_arch)}
|
||||
java_arch=$(
|
||||
"$java_bin" -XshowSettings:properties -version 2>&1 \
|
||||
| sed -n 's/^[[:space:]]*os\.arch = [[:space:]]*//p' \
|
||||
| head -n 1
|
||||
)
|
||||
case "$host_arch:$java_arch" in
|
||||
x86_64:amd64|x86_64:x86_64|x86:x86|x86:i386|x86:i486|x86:i586|x86:i686|\
|
||||
aarch64:aarch64|aarch64:arm64|arm:arm) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -194,6 +242,7 @@ system_java_home() {
|
||||
| head -n 1
|
||||
)
|
||||
[[ "$java_major" =~ ^[0-9]+$ ]] && [ "$java_major" -ge 17 ] || return 1
|
||||
java_arch_matches_host "$java_bin" || return 1
|
||||
resolved_java=$(readlink -f "$java_bin" 2>/dev/null || printf '%s' "$java_bin")
|
||||
case "$resolved_java" in
|
||||
*/bin/java) home=${resolved_java%/bin/java} ;;
|
||||
@@ -204,12 +253,14 @@ system_java_home() {
|
||||
}
|
||||
|
||||
prepare_java() {
|
||||
local api java_metadata package_metadata package_uuid java_url java_sha256 actual_sha256 system_home
|
||||
local api java_metadata package_metadata package_uuid java_url java_sha256 actual_sha256 system_home java_line
|
||||
if system_home=$(system_java_home); then
|
||||
JAVA_STAGED_DIR="$WORK_DIR/java.next"
|
||||
ln -s "$system_home" "$JAVA_STAGED_DIR"
|
||||
log "Using existing Java 17 runtime at $system_home"
|
||||
"$JAVA_STAGED_DIR/bin/java" -version
|
||||
mkdir -p "$JAVA_STAGED_DIR"
|
||||
log "Copying existing Java 17 runtime from $system_home into the managed application directory"
|
||||
COPYFILE_DISABLE=1 cp -R "$system_home/." "$JAVA_STAGED_DIR/"
|
||||
java_line=$("$JAVA_STAGED_DIR/bin/java" -version 2>&1 | head -n 1)
|
||||
log "Local Java verified: $java_line"
|
||||
return 0
|
||||
fi
|
||||
log "No local Java 17 runtime found; downloading the official Azul Java 17 runtime"
|
||||
@@ -231,18 +282,88 @@ prepare_java() {
|
||||
[ "$actual_sha256" = "$java_sha256" ] || die "Java 17 runtime SHA-256 verification failed"
|
||||
mkdir -p "$JAVA_STAGED_DIR"
|
||||
tar -xzf "$WORK_DIR/java.tar.gz" --strip-components=1 -C "$JAVA_STAGED_DIR"
|
||||
"$JAVA_STAGED_DIR/bin/java" -version
|
||||
java_arch_matches_host "$JAVA_STAGED_DIR/bin/java" \
|
||||
|| die "Downloaded Java runtime architecture does not match $HOST_ARCH"
|
||||
java_line=$("$JAVA_STAGED_DIR/bin/java" -version 2>&1 | head -n 1)
|
||||
log "Downloaded Java verified: $java_line"
|
||||
}
|
||||
|
||||
activate_java() {
|
||||
mkdir -p "$APP_ROOT/runtime"
|
||||
JAVA_PREVIOUS_DIR="$WORK_DIR/java.previous"
|
||||
if [ -e "$APP_ROOT/runtime/java" ]; then
|
||||
if [ -e "$APP_ROOT/runtime/java" ] || [ -L "$APP_ROOT/runtime/java" ]; then
|
||||
mv "$APP_ROOT/runtime/java" "$JAVA_PREVIOUS_DIR"
|
||||
fi
|
||||
mv "$JAVA_STAGED_DIR" "$APP_ROOT/runtime/java"
|
||||
chown -R root:"$SERVICE_GROUP" "$APP_ROOT/runtime/java"
|
||||
chmod -R u=rwX,g=rX,o= "$APP_ROOT/runtime/java"
|
||||
JAVA_ACTIVATED=true
|
||||
"$APP_ROOT/runtime/java/bin/java" -version
|
||||
}
|
||||
|
||||
ensure_service_identity() {
|
||||
local existing_home nologin_path
|
||||
command -v getent >/dev/null 2>&1 || die "getent is required"
|
||||
if ! getent group "$SERVICE_GROUP" >/dev/null 2>&1; then
|
||||
groupadd --system "$SERVICE_GROUP"
|
||||
fi
|
||||
if id "$SERVICE_USER" >/dev/null 2>&1; then
|
||||
existing_home=$(getent passwd "$SERVICE_USER" | awk -F: '{print $6}')
|
||||
[ "$existing_home" = "$STATE_ROOT" ] \
|
||||
|| die "Existing user $SERVICE_USER has unexpected home directory $existing_home"
|
||||
if ! id -nG "$SERVICE_USER" | tr ' ' '\n' | grep -Fxq "$SERVICE_GROUP"; then
|
||||
usermod --append --groups "$SERVICE_GROUP" "$SERVICE_USER"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
nologin_path=$(command -v nologin 2>/dev/null || true)
|
||||
[ -n "$nologin_path" ] || nologin_path=/usr/sbin/nologin
|
||||
[ -x "$nologin_path" ] || die "A nologin shell is required"
|
||||
useradd --system --gid "$SERVICE_GROUP" --home-dir "$STATE_ROOT" --shell "$nologin_path" "$SERVICE_USER"
|
||||
}
|
||||
|
||||
prepare_managed_layout() {
|
||||
command -v runuser >/dev/null 2>&1 || die "runuser is required"
|
||||
install -d -o root -g "$SERVICE_GROUP" -m 0750 \
|
||||
"$APP_ROOT" "$APP_ROOT/releases" "$APP_ROOT/runtime" "$APP_ROOT/bin"
|
||||
install -d -o "$SERVICE_USER" -g "$SERVICE_GROUP" -m 0750 \
|
||||
"$STATE_ROOT" "$STATE_ROOT/files" "$STATE_ROOT/tmp" /var/log/kaidi
|
||||
install -d -o "$SERVICE_USER" -g "$SERVICE_GROUP" -m 0700 "$STATE_ROOT/setup"
|
||||
install -d -o root -g "$SERVICE_GROUP" -m 0750 "$UPDATE_STATE_ROOT"
|
||||
install -d -o "$SERVICE_USER" -g "$SERVICE_GROUP" -m 0750 "$UPDATE_STATE_ROOT/inbox"
|
||||
install -d -o root -g root -m 0700 "$CONFIG_ROOT"
|
||||
}
|
||||
|
||||
secure_release_tree() {
|
||||
local release_dir=$1
|
||||
chown -R root:"$SERVICE_GROUP" "$release_dir"
|
||||
find "$release_dir" -type d -exec chmod 0750 {} +
|
||||
find "$release_dir" -type f -exec chmod 0640 {} +
|
||||
chmod 0750 "$release_dir/ops/update.sh"
|
||||
}
|
||||
|
||||
restore_security_contexts() {
|
||||
command -v restorecon >/dev/null 2>&1 || return 0
|
||||
restorecon -RF "$APP_ROOT" "$STATE_ROOT" "$UPDATE_STATE_ROOT" /var/log/kaidi \
|
||||
/etc/systemd/system/kaidi-finance.service \
|
||||
/etc/systemd/system/kaidi-update.service \
|
||||
/etc/systemd/system/kaidi-update.path >/dev/null 2>&1 \
|
||||
|| log "SELinux context restoration reported a warning; service access checks will decide whether installation can continue"
|
||||
}
|
||||
|
||||
verify_service_access() {
|
||||
# shellcheck disable=SC2016 # Positional parameters are expanded by the child shell.
|
||||
runuser -u "$SERVICE_USER" -- sh -c \
|
||||
'cd "$1" && test -r app.jar && test -r public/index.html' sh "$APP_ROOT/current" \
|
||||
|| die "$SERVICE_USER cannot traverse or read the active release"
|
||||
runuser -u "$SERVICE_USER" -- test -x "$APP_ROOT/runtime/java/bin/java" \
|
||||
|| die "$SERVICE_USER cannot execute the managed Java runtime"
|
||||
runuser -u "$SERVICE_USER" -- "$APP_ROOT/runtime/java/bin/java" -version >/dev/null 2>&1 \
|
||||
|| die "The managed Java runtime cannot execute as $SERVICE_USER"
|
||||
runuser -u "$SERVICE_USER" -- test -w "$STATE_ROOT/files" \
|
||||
|| die "$SERVICE_USER cannot write the file-storage directory"
|
||||
runuser -u "$SERVICE_USER" -- test -w "$UPDATE_STATE_ROOT/inbox" \
|
||||
|| die "$SERVICE_USER cannot write the update inbox"
|
||||
log "Service-user filesystem and Java access checks passed"
|
||||
}
|
||||
|
||||
random_secret() { openssl rand -base64 36 | tr -d '\n/+=' | cut -c1-36; }
|
||||
@@ -599,6 +720,12 @@ backup_managed_state() {
|
||||
|
||||
restore_unit_state() {
|
||||
local unit=$1 was_enabled=$2 was_active=$3
|
||||
if ! systemctl cat "$unit" >/dev/null 2>&1; then
|
||||
systemctl reset-failed "$unit" >/dev/null 2>&1 || true
|
||||
[ "$was_enabled" = false ] && [ "$was_active" = false ]
|
||||
return
|
||||
fi
|
||||
systemctl reset-failed "$unit" >/dev/null 2>&1 || true
|
||||
if [ "$was_enabled" = true ]; then
|
||||
systemctl enable "$unit" >/dev/null 2>&1
|
||||
else
|
||||
@@ -631,7 +758,7 @@ rollback_install() {
|
||||
local index=0 path rollback_failed=false
|
||||
set +e
|
||||
log "Installation failed; restoring the previous managed state"
|
||||
systemctl stop kaidi-update.path kaidi-finance.service >/dev/null 2>&1 || rollback_failed=true
|
||||
systemctl stop kaidi-update.path kaidi-finance.service >/dev/null 2>&1 || true
|
||||
for path in "${MANAGED_PATHS[@]}"; do
|
||||
restore_managed_path "$path" "$index" || rollback_failed=true
|
||||
index=$((index + 1))
|
||||
@@ -639,7 +766,7 @@ rollback_install() {
|
||||
if [ -n "$RELEASE_DIR" ]; then
|
||||
rm -rf -- "$RELEASE_DIR" || rollback_failed=true
|
||||
fi
|
||||
if [ -d "$JAVA_PREVIOUS_DIR" ]; then
|
||||
if [ -d "$JAVA_PREVIOUS_DIR" ] || [ -L "$JAVA_PREVIOUS_DIR" ]; then
|
||||
rm -rf -- "$APP_ROOT/runtime/java" || rollback_failed=true
|
||||
mkdir -p "$APP_ROOT/runtime" || rollback_failed=true
|
||||
mv "$JAVA_PREVIOUS_DIR" "$APP_ROOT/runtime/java" || rollback_failed=true
|
||||
@@ -666,8 +793,10 @@ cleanup() {
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
main() {
|
||||
trap cleanup EXIT
|
||||
|
||||
validate_inputs
|
||||
configure_app_port
|
||||
preflight_host
|
||||
install_packages
|
||||
@@ -714,25 +843,18 @@ find "$WORK_DIR/extracted" -type l -print -quit | grep -q . \
|
||||
[ "$(cat "$WORK_DIR/extracted/VERSION")" = "$VERSION" ] || die "Release version mismatch"
|
||||
[ -x "$WORK_DIR/extracted/ops/update.sh" ] || die "Release updater is missing"
|
||||
|
||||
prepare_java
|
||||
configure_database
|
||||
preflight_database
|
||||
|
||||
id kaidi >/dev/null 2>&1 || useradd --system --home "$STATE_ROOT" --shell /usr/sbin/nologin kaidi
|
||||
mkdir -p "$APP_ROOT/releases" "$APP_ROOT/bin" "$STATE_ROOT/files" "$STATE_ROOT/tmp" \
|
||||
"$STATE_ROOT/setup" "$UPDATE_STATE_ROOT/inbox" "$CONFIG_ROOT" /var/log/kaidi
|
||||
chown -R kaidi:kaidi "$STATE_ROOT" "$UPDATE_STATE_ROOT/inbox" /var/log/kaidi
|
||||
chown root:kaidi "$UPDATE_STATE_ROOT"
|
||||
chmod 0750 "$STATE_ROOT" "$STATE_ROOT/files" "$STATE_ROOT/tmp" "$UPDATE_STATE_ROOT" "$UPDATE_STATE_ROOT/inbox"
|
||||
chmod 0700 "$STATE_ROOT/setup"
|
||||
ensure_service_identity
|
||||
prepare_managed_layout
|
||||
prepare_java
|
||||
|
||||
RELEASE_DIR="$APP_ROOT/releases/$VERSION"
|
||||
[ ! -e "$RELEASE_DIR" ] || RELEASE_DIR="$APP_ROOT/releases/${VERSION}-reinstall-$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
backup_managed_state
|
||||
activate_java
|
||||
mv "$WORK_DIR/extracted" "$RELEASE_DIR"
|
||||
chown -R root:root "$RELEASE_DIR"
|
||||
chmod -R go-w "$RELEASE_DIR"
|
||||
secure_release_tree "$RELEASE_DIR"
|
||||
ln -sfn "$RELEASE_DIR" "$APP_ROOT/current.next"
|
||||
mv -Tf "$APP_ROOT/current.next" "$APP_ROOT/current"
|
||||
install -m 0755 "$RELEASE_DIR/ops/update.sh" "$APP_ROOT/bin/update.sh"
|
||||
@@ -824,7 +946,15 @@ chmod 0600 "$CONFIG_ROOT/update.env"
|
||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-finance.service" /etc/systemd/system/kaidi-finance.service
|
||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.service" /etc/systemd/system/kaidi-update.service
|
||||
install -m 0644 "$RELEASE_DIR/ops/kaidi-update.path" /etc/systemd/system/kaidi-update.path
|
||||
restore_security_contexts
|
||||
verify_service_access
|
||||
systemd-analyze verify \
|
||||
/etc/systemd/system/kaidi-finance.service \
|
||||
/etc/systemd/system/kaidi-update.service \
|
||||
/etc/systemd/system/kaidi-update.path >/dev/null \
|
||||
|| die "Installed systemd units failed validation"
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed kaidi-finance.service >/dev/null 2>&1 || true
|
||||
systemctl enable kaidi-finance.service
|
||||
systemctl restart kaidi-finance.service
|
||||
wait_for_health
|
||||
@@ -875,3 +1005,6 @@ if [ "$REINSTALL" != true ] && [ "$SETUP_WIZARD" != true ]; then
|
||||
log "Temporary credentials: /root/kaidi-first-login.txt"
|
||||
log "Change the temporary password immediately after first sign-in"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Description=Kaidi Finance System
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
StartLimitIntervalSec=60
|
||||
StartLimitBurst=3
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
+53
-5
@@ -18,6 +18,8 @@ RELEASE_API_URL=${UPDATE_RELEASE_API_URL:-}
|
||||
RELEASE_TOKEN=${UPDATE_RELEASE_TOKEN:-}
|
||||
CACHE_ROOT=${KAIDI_UPDATE_CACHE_ROOT:-$STATE_ROOT/cache}
|
||||
SERVICE_NAME=${KAIDI_SERVICE_NAME:-kaidi-finance.service}
|
||||
SERVICE_USER=${KAIDI_SERVICE_USER:-kaidi}
|
||||
SERVICE_GROUP=${KAIDI_SERVICE_GROUP:-kaidi}
|
||||
HEALTH_URL=${KAIDI_HEALTH_URL:-http://127.0.0.1:18080/actuator/health}
|
||||
APP_INDEX_URL=${KAIDI_APP_INDEX_URL:-http://127.0.0.1:18080/}
|
||||
LOCK_FILE=$STATE_ROOT/update.lock
|
||||
@@ -38,9 +40,10 @@ case "$HEALTH_ATTEMPTS:$HEALTH_INTERVAL_SECONDS" in
|
||||
esac
|
||||
[ "$HEALTH_ATTEMPTS" -ge 1 ] || { printf '%s\n' "Update health-check attempts must be at least 1" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$STATE_ROOT/inbox" "$PROCESSING_DIR" "$FAILED_REQUEST_ROOT" "$TRANSACTION_ROOT" \
|
||||
"$STATE_ROOT/work" "$APP_ROOT/releases" "$LOG_ROOT" "$BACKUP_ROOT" "$CACHE_ROOT"
|
||||
chmod 0700 "$PROCESSING_DIR" "$FAILED_REQUEST_ROOT" "$TRANSACTION_ROOT" "$BACKUP_ROOT" "$CACHE_ROOT"
|
||||
bootstrap_die() {
|
||||
printf '%s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
status() {
|
||||
state=$1
|
||||
@@ -77,6 +80,43 @@ fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
prepare_update_layout() {
|
||||
id "$SERVICE_USER" >/dev/null 2>&1 || bootstrap_die "Service user $SERVICE_USER is missing"
|
||||
id -nG "$SERVICE_USER" | tr ' ' '\n' | grep -Fxq "$SERVICE_GROUP" \
|
||||
|| bootstrap_die "Service user $SERVICE_USER is not a member of group $SERVICE_GROUP"
|
||||
command -v runuser >/dev/null 2>&1 || bootstrap_die "runuser is required"
|
||||
install -d -o root -g "$SERVICE_GROUP" -m 0750 \
|
||||
"$APP_ROOT" "$APP_ROOT/releases" "$APP_ROOT/runtime" "$APP_ROOT/bin" "$STATE_ROOT" \
|
||||
|| bootstrap_die "Managed application or update directories could not be prepared"
|
||||
install -d -o "$SERVICE_USER" -g "$SERVICE_GROUP" -m 0750 "$STATE_ROOT/inbox" "$LOG_ROOT" \
|
||||
|| bootstrap_die "Writable update directories could not be prepared"
|
||||
install -d -o root -g root -m 0700 \
|
||||
"$PROCESSING_DIR" "$FAILED_REQUEST_ROOT" "$TRANSACTION_ROOT" "$STATE_ROOT/work" \
|
||||
"$BACKUP_ROOT" "$CACHE_ROOT" \
|
||||
|| bootstrap_die "Private update directories could not be prepared"
|
||||
}
|
||||
|
||||
secure_release_tree() {
|
||||
release_dir=$1
|
||||
chown -R root:"$SERVICE_GROUP" "$release_dir" || return 1
|
||||
find "$release_dir" -type d -exec chmod 0750 {} + || return 1
|
||||
find "$release_dir" -type f -exec chmod 0640 {} + || return 1
|
||||
chmod 0750 "$release_dir/ops/update.sh"
|
||||
}
|
||||
|
||||
verify_release_access() {
|
||||
release_dir=$1
|
||||
# shellcheck disable=SC2016 # Positional parameters are expanded by the child shell.
|
||||
runuser -u "$SERVICE_USER" -- sh -c \
|
||||
'cd "$1" && test -r app.jar && test -r public/index.html' sh "$release_dir" \
|
||||
|| return 1
|
||||
runuser -u "$SERVICE_USER" -- test -x "$APP_ROOT/runtime/java/bin/java" \
|
||||
|| return 1
|
||||
runuser -u "$SERVICE_USER" -- "$APP_ROOT/runtime/java/bin/java" -version >/dev/null 2>&1
|
||||
}
|
||||
|
||||
prepare_update_layout
|
||||
|
||||
# shellcheck disable=SC2329 # Invoked by the EXIT trap below.
|
||||
cleanup() {
|
||||
rc=$?
|
||||
@@ -329,6 +369,7 @@ rollback_active_transaction() {
|
||||
rollback_ok=false
|
||||
fi
|
||||
restore_operations || rollback_ok=false
|
||||
systemctl reset-failed "$SERVICE_NAME" >/dev/null 2>&1 || true
|
||||
systemctl start "$SERVICE_NAME" || rollback_ok=false
|
||||
if [ "$rollback_ok" = true ] && verify_app_surface; then
|
||||
remove_failed_release "$failed_release"
|
||||
@@ -520,8 +561,14 @@ if [ -e "$RELEASE_DIR" ]; then
|
||||
rm -rf "$RELEASE_DIR" || fail "Failed release staging directory could not be cleaned"
|
||||
fi
|
||||
mv "$WORK_DIR/extracted" "$RELEASE_DIR" || fail "Release directory could not be activated"
|
||||
chown -R root:root "$RELEASE_DIR" || fail "Release ownership could not be secured"
|
||||
chmod -R go-w "$RELEASE_DIR" || fail "Release permissions could not be secured"
|
||||
secure_release_tree "$RELEASE_DIR" || fail "Release ownership or permissions could not be secured"
|
||||
if command -v restorecon >/dev/null 2>&1; then
|
||||
restorecon -RF "$APP_ROOT" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if ! verify_release_access "$RELEASE_DIR"; then
|
||||
rm -rf "$RELEASE_DIR"
|
||||
fail "Service user cannot access the release or managed Java runtime"
|
||||
fi
|
||||
|
||||
PREVIOUS_TARGET=$(readlink "$APP_ROOT/current" 2>/dev/null || true)
|
||||
mkdir "$ACTIVE_TRANSACTION"
|
||||
@@ -552,6 +599,7 @@ write_transaction_value phase APP_SWITCHED
|
||||
|
||||
status RUNNING "Starting and verifying release $TARGET_VERSION" "$TARGET_VERSION"
|
||||
write_transaction_value phase HEALTH_CHECKING
|
||||
systemctl reset-failed "$SERVICE_NAME" >/dev/null 2>&1 || true
|
||||
if systemctl start "$SERVICE_NAME" \
|
||||
&& systemctl is-active --quiet kaidi-update.path \
|
||||
&& verify_app_surface; then
|
||||
|
||||
Reference in New Issue
Block a user