mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-31 10:14:15 +00:00
Random PostgreSQL role + post-install credentials display (#4608)
* feat(install): random PostgreSQL role + post-install credentials display The local-Postgres installer used to bake in a static role name (`xui`) and only printed `PostgreSQL (xui@127.0.0.1:5432/xui)` at the end of install, leaving operators without the random password or any hint of how to connect from the shell. Two changes: - install_postgres_local now generates an 8-char random role name alongside the random password, and double-quotes identifiers in the CREATE/ALTER statements (a random alphanumeric may start with a digit, which Postgres rejects for unquoted identifiers). - After a successful local install, a dedicated "PostgreSQL Credentials" block is rendered in the summary — DB / user / pass / host / port / DSN / env-file path, plus ready-to-paste psql commands for both the postgres superuser and the new role. Credentials cross the subshell boundary via a 0600 tmpfile (PG_CRED_FILE) that the parent shell sources and unlinks; the PG_* vars are unset after display. Only fires for the local-install flow; the external-DSN path is unchanged. * fix(install): address Copilot review on Postgres install flow - Use mktemp (unguessable, 0600) instead of /tmp/x-ui-pg-creds.$$ and cleanup in both success and failure paths to close the symlink/race attack on the predictable filename. - In install_postgres_local, capture the prior umask and restore it after writing PG_CRED_FILE; return 1 if the write fails so the caller does not source nothing and label the install with empty PG_* vars. - On reinstall, reuse the existing xui DB owner instead of generating a fresh role each run, so existing tables stay accessible after a re-run; only the password is rotated. Falls back to a fresh random role when the DB does not exist or is owned by postgres.
This commit is contained in:
parent
c03ecfe638
commit
058c030e81
1 changed files with 88 additions and 13 deletions
101
install.sh
101
install.sh
|
|
@ -111,10 +111,11 @@ gen_random_string() {
|
||||||
}
|
}
|
||||||
|
|
||||||
install_postgres_local() {
|
install_postgres_local() {
|
||||||
local pg_user="xui"
|
local pg_user pg_pass
|
||||||
local pg_db="xui"
|
|
||||||
local pg_pass
|
|
||||||
pg_pass=$(gen_random_string 24)
|
pg_pass=$(gen_random_string 24)
|
||||||
|
local pg_db="xui"
|
||||||
|
local pg_host="127.0.0.1"
|
||||||
|
local pg_port="5432"
|
||||||
|
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
|
|
@ -170,20 +171,50 @@ install_postgres_local() {
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
# Idempotent role/db creation.
|
local existing_owner=""
|
||||||
|
existing_owner=$(sudo -u postgres psql -tAc \
|
||||||
|
"SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_database WHERE datname='${pg_db}'" 2> /dev/null \
|
||||||
|
| tr -d '[:space:]')
|
||||||
|
if [[ -n "${existing_owner}" && "${existing_owner}" != "postgres" ]]; then
|
||||||
|
pg_user="${existing_owner}"
|
||||||
|
else
|
||||||
|
pg_user=$(gen_random_string 8)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Idempotent role/db creation. Identifiers are double-quoted because a
|
||||||
|
# random username may start with a digit, which Postgres rejects unquoted.
|
||||||
sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${pg_user}'" 2> /dev/null \
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='${pg_user}'" 2> /dev/null \
|
||||||
| grep -q 1 \
|
| grep -q 1 \
|
||||||
|| sudo -u postgres psql -c "CREATE USER ${pg_user} WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
|| sudo -u postgres psql -c "CREATE USER \"${pg_user}\" WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
||||||
|
|
||||||
sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${pg_db}'" 2> /dev/null \
|
sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='${pg_db}'" 2> /dev/null \
|
||||||
| grep -q 1 \
|
| grep -q 1 \
|
||||||
|| sudo -u postgres psql -c "CREATE DATABASE ${pg_db} OWNER ${pg_user};" >&2 || return 1
|
|| sudo -u postgres psql -c "CREATE DATABASE \"${pg_db}\" OWNER \"${pg_user}\";" >&2 || return 1
|
||||||
|
|
||||||
sudo -u postgres psql -c "ALTER USER ${pg_user} WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
sudo -u postgres psql -c "ALTER USER \"${pg_user}\" WITH PASSWORD '${pg_pass}';" >&2 || return 1
|
||||||
|
|
||||||
local pg_pass_enc
|
local pg_pass_enc
|
||||||
pg_pass_enc=$(printf '%s' "${pg_pass}" | sed -e 's/%/%25/g' -e 's/:/%3A/g' -e 's/@/%40/g' -e 's|/|%2F|g' -e 's/?/%3F/g' -e 's/#/%23/g')
|
pg_pass_enc=$(printf '%s' "${pg_pass}" | sed -e 's/%/%25/g' -e 's/:/%3A/g' -e 's/@/%40/g' -e 's|/|%2F|g' -e 's/?/%3F/g' -e 's/#/%23/g')
|
||||||
echo "postgres://${pg_user}:${pg_pass_enc}@127.0.0.1:5432/${pg_db}?sslmode=disable"
|
|
||||||
|
if [[ -n "${PG_CRED_FILE:-}" ]]; then
|
||||||
|
local prev_umask
|
||||||
|
prev_umask=$(umask)
|
||||||
|
umask 077
|
||||||
|
if ! cat > "${PG_CRED_FILE}" << EOF; then
|
||||||
|
PG_USER=${pg_user}
|
||||||
|
PG_PASS=${pg_pass}
|
||||||
|
PG_HOST=${pg_host}
|
||||||
|
PG_PORT=${pg_port}
|
||||||
|
PG_DB=${pg_db}
|
||||||
|
EOF
|
||||||
|
umask "${prev_umask}"
|
||||||
|
echo -e "${red}Failed to write PostgreSQL credentials to ${PG_CRED_FILE}${plain}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
umask "${prev_umask}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "postgres://${pg_user}:${pg_pass_enc}@${pg_host}:${pg_port}/${pg_db}?sslmode=disable"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -823,7 +854,7 @@ config_after_install() {
|
||||||
echo -e "${green}═══════════════════════════════════════════${plain}"
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
echo -e "${green} Database Selection ${plain}"
|
echo -e "${green} Database Selection ${plain}"
|
||||||
echo -e "${green}═══════════════════════════════════════════${plain}"
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
echo -e " 1) SQLite (default — recommended for < 1000 clients)"
|
echo -e " 1) SQLite (default — recommended for < 500 clients)"
|
||||||
echo -e " 2) PostgreSQL (recommended for high client counts / many nodes)"
|
echo -e " 2) PostgreSQL (recommended for high client counts / many nodes)"
|
||||||
read -rp "Choose [1]: " db_choice
|
read -rp "Choose [1]: " db_choice
|
||||||
db_choice="${db_choice:-1}"
|
db_choice="${db_choice:-1}"
|
||||||
|
|
@ -843,6 +874,7 @@ config_after_install() {
|
||||||
|
|
||||||
local xui_dsn=""
|
local xui_dsn=""
|
||||||
local pg_mode=""
|
local pg_mode=""
|
||||||
|
local pg_local_installed=0
|
||||||
while [[ -z "$xui_dsn" ]]; do
|
while [[ -z "$xui_dsn" ]]; do
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " 1) Install PostgreSQL locally and create a dedicated user/db (recommended)"
|
echo -e " 1) Install PostgreSQL locally and create a dedicated user/db (recommended)"
|
||||||
|
|
@ -857,9 +889,23 @@ config_after_install() {
|
||||||
db_label="PostgreSQL (external)"
|
db_label="PostgreSQL (external)"
|
||||||
else
|
else
|
||||||
echo -e "${yellow}Installing PostgreSQL — this may take a moment...${plain}"
|
echo -e "${yellow}Installing PostgreSQL — this may take a moment...${plain}"
|
||||||
if xui_dsn=$(install_postgres_local); then
|
local pg_cred_file
|
||||||
db_label="PostgreSQL (xui@127.0.0.1:5432/xui)"
|
pg_cred_file=$(mktemp 2> /dev/null) || pg_cred_file=$(mktemp -t x-ui-pg-creds.XXXXXXXX)
|
||||||
|
if [[ -z "${pg_cred_file}" ]]; then
|
||||||
|
echo -e "${red}Failed to create temporary credentials file.${plain}"
|
||||||
|
xui_dsn=""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if xui_dsn=$(PG_CRED_FILE="${pg_cred_file}" install_postgres_local); then
|
||||||
|
pg_local_installed=1
|
||||||
|
if [[ -r "${pg_cred_file}" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "${pg_cred_file}"
|
||||||
|
fi
|
||||||
|
rm -f "${pg_cred_file}"
|
||||||
|
db_label="PostgreSQL (${PG_USER}@${PG_HOST}:${PG_PORT}/${PG_DB})"
|
||||||
else
|
else
|
||||||
|
rm -f "${pg_cred_file}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${red}PostgreSQL installation failed.${plain}"
|
echo -e "${red}PostgreSQL installation failed.${plain}"
|
||||||
echo -e " 1) Retry local install"
|
echo -e " 1) Retry local install"
|
||||||
|
|
@ -870,8 +916,15 @@ config_after_install() {
|
||||||
pg_fail="${pg_fail:-1}"
|
pg_fail="${pg_fail:-1}"
|
||||||
case "$pg_fail" in
|
case "$pg_fail" in
|
||||||
2) pg_mode="2" ;;
|
2) pg_mode="2" ;;
|
||||||
3) echo -e "${red}Install aborted.${plain}"; exit 1 ;;
|
3)
|
||||||
4) db_choice="1"; xui_dsn=""; break ;;
|
echo -e "${red}Install aborted.${plain}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
db_choice="1"
|
||||||
|
xui_dsn=""
|
||||||
|
break
|
||||||
|
;;
|
||||||
*) xui_dsn="" ;;
|
*) xui_dsn="" ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
@ -935,6 +988,28 @@ EOF
|
||||||
else
|
else
|
||||||
echo -e "${yellow}⚠ SSL Certificate: Skipped — panel is HTTP-only. Use a reverse proxy or SSH tunnel.${plain}"
|
echo -e "${yellow}⚠ SSL Certificate: Skipped — panel is HTTP-only. Use a reverse proxy or SSH tunnel.${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$db_choice" == "2" && "$pg_local_installed" == "1" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
|
echo -e "${green} PostgreSQL Credentials ${plain}"
|
||||||
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
|
echo -e "${green}DB Name: ${PG_DB}${plain}"
|
||||||
|
echo -e "${green}Username: ${PG_USER}${plain}"
|
||||||
|
echo -e "${green}Password: ${PG_PASS}${plain}"
|
||||||
|
echo -e "${green}Host: ${PG_HOST}${plain}"
|
||||||
|
echo -e "${green}Port: ${PG_PORT}${plain}"
|
||||||
|
echo -e "${green}DSN: ${xui_dsn}${plain}"
|
||||||
|
echo -e "${green}Env file: ${xui_env_file}${plain}"
|
||||||
|
echo -e "${green}-------------------------------------------${plain}"
|
||||||
|
echo -e "${green}Connect from this server:${plain}"
|
||||||
|
echo -e " ${blue}sudo -u postgres psql -d ${PG_DB}${plain} (as the postgres superuser)"
|
||||||
|
echo -e " ${blue}PGPASSWORD='${PG_PASS}' psql -h ${PG_HOST} -p ${PG_PORT} -U ${PG_USER} -d ${PG_DB}${plain}"
|
||||||
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
|
echo -e "${yellow}⚠ The panel reads these credentials from ${xui_env_file}.${plain}"
|
||||||
|
echo -e "${yellow}⚠ Save the password — it is not stored anywhere else in plain text.${plain}"
|
||||||
|
unset PG_USER PG_PASS PG_HOST PG_PORT PG_DB
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
local config_webBasePath=$(gen_random_string 18)
|
local config_webBasePath=$(gen_random_string 18)
|
||||||
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
|
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue