mirror of
https://github.com/okxlin/appstore.git
synced 2026-09-23 00:00:59 +00:00
94 lines
3.5 KiB
Bash
Executable File
94 lines
3.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
|
ENV_FILE="${ENV_FILE:-${ROOT_DIR}/.env}"
|
|
|
|
fail() {
|
|
printf '%s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
read_env_value() {
|
|
local key="$1"
|
|
local value=""
|
|
value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
|
|
case "$value" in
|
|
\"*\") value="${value#\"}"; value="${value%\"}" ;;
|
|
\'*\') value="${value#\'}"; value="${value%\'}" ;;
|
|
esac
|
|
printf '%s\n' "$value"
|
|
}
|
|
|
|
effective_value() {
|
|
local key="$1"
|
|
if [[ -v "$key" ]]; then
|
|
printf '%s\n' "${!key}"
|
|
else
|
|
read_env_value "$key"
|
|
fi
|
|
}
|
|
|
|
resolve_data_dir() {
|
|
local raw="$1"
|
|
if [[ "$raw" == /* ]]; then
|
|
realpath -m -- "$raw"
|
|
else
|
|
realpath -m -- "${ROOT_DIR}/${raw#./}"
|
|
fi
|
|
}
|
|
|
|
[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must be a regular file"
|
|
command -v openssl >/dev/null 2>&1 || fail "openssl is required to generate the passwd file"
|
|
|
|
username="$(effective_value AUTH_USERNAME)"
|
|
password="$(effective_value AUTH_PASSWORD)"
|
|
auth_host="$(effective_value AUTH_HOST)"
|
|
token_secret="$(effective_value TOKEN_SECRET)"
|
|
cookie_secure="$(effective_value COOKIE_SECURE)"
|
|
pass_user_header="$(effective_value PASS_USER_HEADER)"
|
|
data_raw="$(effective_value APP_DATA_DIR)"
|
|
|
|
[[ "$username" =~ ^[A-Za-z0-9._-]{1,64}$ ]] || fail "AUTH_USERNAME contains unsupported characters"
|
|
[[ ${#password} -ge 12 && ${#password} -le 256 ]] || fail "AUTH_PASSWORD must contain 12 to 256 characters"
|
|
[[ ! "$password" =~ [[:cntrl:]] ]] || fail "AUTH_PASSWORD must not contain control characters"
|
|
[[ "$auth_host" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*(:[0-9]{1,5})?$ ]] || fail "AUTH_HOST must be a hostname with an optional port and no URL scheme"
|
|
[[ ${#token_secret} -ge 32 && ${#token_secret} -le 512 ]] || fail "TOKEN_SECRET must contain 32 to 512 characters"
|
|
[[ ! "$token_secret" =~ [[:cntrl:]] ]] || fail "TOKEN_SECRET must not contain control characters"
|
|
[[ "$cookie_secure" == true || "$cookie_secure" == false ]] || fail "COOKIE_SECURE must be true or false"
|
|
[[ "$pass_user_header" == true || "$pass_user_header" == false ]] || fail "PASS_USER_HEADER must be true or false"
|
|
[[ -n "$data_raw" && ! "$data_raw" =~ [[:cntrl:]\`\$] ]] || fail "APP_DATA_DIR is invalid"
|
|
|
|
data_dir="$(resolve_data_dir "$data_raw")"
|
|
case "$data_dir" in
|
|
/ | /bin | /boot | /dev | /etc | /home | /lib | /lib64 | /opt | /proc | /root | /run | /sbin | /srv | /sys | /tmp | /usr | /var | /workspace)
|
|
fail "APP_DATA_DIR must be a dedicated subdirectory"
|
|
;;
|
|
esac
|
|
if [[ "$data_raw" != /* ]]; then
|
|
case "$data_dir" in
|
|
"${ROOT_DIR}"/*) ;;
|
|
*) fail "Relative APP_DATA_DIR must stay inside the application version directory" ;;
|
|
esac
|
|
fi
|
|
[[ ! -L "$data_dir" ]] || fail "APP_DATA_DIR must not be a symbolic link"
|
|
[[ ! -e "$data_dir" || -d "$data_dir" ]] || fail "APP_DATA_DIR must be a directory"
|
|
install -d -m 0750 -- "$data_dir"
|
|
|
|
passwd_file="${data_dir}/passwd"
|
|
[[ ! -L "$passwd_file" ]] || fail "passwd must not be a symbolic link"
|
|
[[ ! -e "$passwd_file" || -f "$passwd_file" ]] || fail "passwd must be a regular file"
|
|
|
|
salt="$(openssl rand -hex 8)"
|
|
[[ "$salt" =~ ^[0-9a-f]{16}$ ]] || fail "failed to generate a password salt"
|
|
password_hash="$(printf '%s\n' "$password" | openssl passwd -6 -stdin -salt "rounds=200000\$${salt}")"
|
|
[[ "$password_hash" == "\$6\$rounds=200000\$"* ]] || fail "failed to generate a SHA-512 password hash"
|
|
|
|
umask 077
|
|
temporary="$(mktemp "${data_dir}/.passwd.tmp.XXXXXX")"
|
|
trap 'rm -f -- "${temporary:-}"' EXIT
|
|
printf '%s:%s\n' "$username" "$password_hash" >"$temporary"
|
|
chmod 0600 -- "$temporary"
|
|
mv -f -- "$temporary" "$passwd_file"
|
|
trap - EXIT
|