Safe Update
This commit is contained in:
236
change_admin_password.sh
Normal file
236
change_admin_password.sh
Normal file
@@ -0,0 +1,236 @@
|
||||
#!/bin/bash
|
||||
# DragonCoreSSH V40 admin password recovery tool.
|
||||
# Usage:
|
||||
# sudo bash change_admin_password.sh
|
||||
# sudo bash change_admin_password.sh admin 'NewPasswordHere'
|
||||
# sudo bash change_admin_password.sh --user admin --generate
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
info() { echo -e "${GREEN}[+]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
||||
error() { echo -e "${RED}[x]${NC} $*"; exit 1; }
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/sshpanel}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-sshpanel}"
|
||||
ENV_FILE="${ENV_FILE:-${INSTALL_DIR}/.env}"
|
||||
ADMIN_USER=""
|
||||
NEW_PASSWORD=""
|
||||
GENERATE_PASSWORD=false
|
||||
NO_RESTART=false
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
DragonCoreSSH V40 admin password recovery
|
||||
|
||||
Usage:
|
||||
sudo bash $0
|
||||
sudo bash $0 admin 'NewPasswordHere'
|
||||
sudo bash $0 --user admin --password 'NewPasswordHere'
|
||||
sudo bash $0 --user admin --generate
|
||||
|
||||
Options:
|
||||
-u, --user USERNAME Admin username to reset. Default: admin
|
||||
-p, --password PASSWORD New password. If omitted, you will be prompted.
|
||||
-g, --generate Generate a strong random password.
|
||||
--no-restart Do not restart the sshpanel service after changing DB.
|
||||
-h, --help Show this help.
|
||||
|
||||
Environment overrides:
|
||||
INSTALL_DIR=/opt/sshpanel
|
||||
ENV_FILE=/opt/sshpanel/.env
|
||||
SERVICE_NAME=sshpanel
|
||||
USAGE
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-u|--user)
|
||||
[[ $# -ge 2 ]] || error "Missing value for $1"
|
||||
ADMIN_USER="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--password)
|
||||
[[ $# -ge 2 ]] || error "Missing value for $1"
|
||||
NEW_PASSWORD="$2"
|
||||
shift 2
|
||||
;;
|
||||
-g|--generate)
|
||||
GENERATE_PASSWORD=true
|
||||
shift
|
||||
;;
|
||||
--no-restart)
|
||||
NO_RESTART=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-* )
|
||||
error "Unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$ADMIN_USER" ]]; then
|
||||
ADMIN_USER="$1"
|
||||
elif [[ -z "$NEW_PASSWORD" ]]; then
|
||||
NEW_PASSWORD="$1"
|
||||
else
|
||||
error "Too many positional arguments. Use --help for usage."
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ $EUID -ne 0 ]] && error "Run as root: sudo bash $0"
|
||||
[[ -f "$ENV_FILE" ]] || error "Environment file not found: $ENV_FILE"
|
||||
command -v psql >/dev/null 2>&1 || error "psql not found. Install PostgreSQL client first."
|
||||
|
||||
get_env_value() {
|
||||
local key="$1"
|
||||
awk -v key="$key" '
|
||||
$0 ~ "^" key "=" {
|
||||
sub("^[^=]*=", "")
|
||||
gsub(/^\"|\"$/, "")
|
||||
gsub(/^\047|\047$/, "")
|
||||
print
|
||||
exit
|
||||
}
|
||||
' "$ENV_FILE"
|
||||
}
|
||||
|
||||
update_env_password() {
|
||||
local new_password="$1"
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
awk -v line="ADMIN_PASSWORD=${new_password}" '
|
||||
BEGIN { done = 0 }
|
||||
/^ADMIN_PASSWORD=/ { print line; done = 1; next }
|
||||
{ print }
|
||||
END { if (!done) print line }
|
||||
' "$ENV_FILE" > "$tmp"
|
||||
cat "$tmp" > "$ENV_FILE"
|
||||
rm -f "$tmp"
|
||||
chmod 600 "$ENV_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
local pw=""
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
pw="$(openssl rand -base64 24 | tr -d '\n' | tr -d '=/+' | head -c 24 || true)"
|
||||
fi
|
||||
if [[ ${#pw} -lt 20 ]]; then
|
||||
pw="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 24 || true)"
|
||||
fi
|
||||
if [[ ${#pw} -lt 20 ]]; then
|
||||
pw="DragonCore$(date +%s%N)"
|
||||
fi
|
||||
printf '%s' "$pw"
|
||||
}
|
||||
|
||||
hash_password() {
|
||||
local pw="$1"
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
printf '%s' "$pw" | sha256sum | awk '{print $1}'
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
printf '%s' "$pw" | shasum -a 256 | awk '{print $1}'
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
printf '%s' "$pw" | openssl dgst -sha256 -r | awk '{print $1}'
|
||||
else
|
||||
error "No SHA-256 tool found. Install coreutils or openssl."
|
||||
fi
|
||||
}
|
||||
|
||||
PG_DSN="$(get_env_value PG_DSN)"
|
||||
[[ -n "$PG_DSN" ]] || error "PG_DSN not found inside $ENV_FILE"
|
||||
|
||||
if [[ -z "$ADMIN_USER" ]]; then
|
||||
read -r -p "Admin username [admin]: " ADMIN_USER
|
||||
ADMIN_USER="${ADMIN_USER:-admin}"
|
||||
fi
|
||||
|
||||
[[ -n "$ADMIN_USER" ]] || error "Admin username cannot be empty."
|
||||
|
||||
if $GENERATE_PASSWORD; then
|
||||
NEW_PASSWORD="$(generate_password)"
|
||||
elif [[ -z "$NEW_PASSWORD" ]]; then
|
||||
read -r -s -p "New password: " PASS1
|
||||
echo
|
||||
read -r -s -p "Confirm password: " PASS2
|
||||
echo
|
||||
[[ "$PASS1" == "$PASS2" ]] || error "Passwords do not match."
|
||||
NEW_PASSWORD="$PASS1"
|
||||
fi
|
||||
|
||||
[[ -n "$NEW_PASSWORD" ]] || error "Password cannot be empty."
|
||||
if [[ ${#NEW_PASSWORD} -lt 8 ]]; then
|
||||
error "Password must have at least 8 characters."
|
||||
fi
|
||||
|
||||
PASSWORD_HASH="$(hash_password "$NEW_PASSWORD")"
|
||||
[[ ${#PASSWORD_HASH} -eq 64 ]] || error "Failed to generate valid SHA-256 password hash."
|
||||
|
||||
info "Updating admin user '${ADMIN_USER}' in PostgreSQL..."
|
||||
psql "$PG_DSN" -v ON_ERROR_STOP=1 \
|
||||
-v admin_user="$ADMIN_USER" \
|
||||
-v password_hash="$PASSWORD_HASH" <<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS admin_users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'reseller',
|
||||
max_users INT NOT NULL DEFAULT 30,
|
||||
expires_at TIMESTAMPTZ,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
INSERT INTO admin_users (username, password_hash, role, max_users, expires_at, is_active)
|
||||
VALUES (:'admin_user', :'password_hash', 'superadmin', 0, NULL, TRUE)
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
password_hash = EXCLUDED.password_hash,
|
||||
role = 'superadmin',
|
||||
max_users = 0,
|
||||
expires_at = NULL,
|
||||
is_active = TRUE;
|
||||
SQL
|
||||
|
||||
if [[ "$ADMIN_USER" == "admin" ]]; then
|
||||
update_env_password "$NEW_PASSWORD"
|
||||
info "Updated ADMIN_PASSWORD inside $ENV_FILE"
|
||||
else
|
||||
warn "ADMIN_PASSWORD in $ENV_FILE was not changed because username is not 'admin'."
|
||||
fi
|
||||
|
||||
if ! $NO_RESTART; then
|
||||
info "Restarting ${SERVICE_NAME} so the in-memory admin cache reloads..."
|
||||
if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files "${SERVICE_NAME}.service" >/dev/null 2>&1; then
|
||||
systemctl restart "$SERVICE_NAME"
|
||||
sleep 1
|
||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||
info "${SERVICE_NAME} restarted successfully."
|
||||
else
|
||||
warn "${SERVICE_NAME} is not active after restart. Last logs:"
|
||||
journalctl -u "$SERVICE_NAME" -n 30 --no-pager 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
elif command -v service >/dev/null 2>&1; then
|
||||
service "$SERVICE_NAME" restart || warn "Could not restart ${SERVICE_NAME}. Restart it manually."
|
||||
else
|
||||
warn "Could not restart ${SERVICE_NAME}. Restart it manually before logging in."
|
||||
fi
|
||||
else
|
||||
warn "Service restart skipped. Restart ${SERVICE_NAME} manually before logging in."
|
||||
fi
|
||||
|
||||
echo
|
||||
info "Admin password changed."
|
||||
echo " Username : ${ADMIN_USER}"
|
||||
echo " Password : ${NEW_PASSWORD}"
|
||||
echo
|
||||
warn "Save this password now. It is only shown here."
|
||||
Reference in New Issue
Block a user