From 98dc59aa9f516eba10f2db3fbef475c2825d9530 Mon Sep 17 00:00:00 2001 From: ANHELL-dev Date: Sun, 22 Feb 2026 23:58:01 +0300 Subject: [PATCH 1/4] feat: panel protection with secret path + basicauth --- data/caddy/caddyfile-protected | 22 ++++++ scripts/remnawave/install-caddy.sh | 112 ++++++++++++++++++++++++++++- scripts/remnawave/install-full.sh | 107 ++++++++++++++++++++++++++- 3 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 data/caddy/caddyfile-protected diff --git a/data/caddy/caddyfile-protected b/data/caddy/caddyfile-protected new file mode 100644 index 0000000..141ed54 --- /dev/null +++ b/data/caddy/caddyfile-protected @@ -0,0 +1,22 @@ +https://$PANEL_DOMAIN { + handle /api/* { + reverse_proxy http://remnawave:$PANEL_PORT + } + + handle /$LOGIN_ROUTE* { + basicauth { + $ADMIN_LOGIN $ADMIN_PASSWORD_HASH + } + uri strip_prefix /$LOGIN_ROUTE + reverse_proxy http://remnawave:$PANEL_PORT + } + + handle { + respond 404 + } +} + +:443 { + tls internal + respond 204 +} diff --git a/scripts/remnawave/install-caddy.sh b/scripts/remnawave/install-caddy.sh index 8cce5dd..87832f0 100644 --- a/scripts/remnawave/install-caddy.sh +++ b/scripts/remnawave/install-caddy.sh @@ -5,6 +5,7 @@ source "/opt/remnasetup/scripts/common/functions.sh" source "/opt/remnasetup/scripts/common/languages.sh" REINSTALL_CADDY=false +NEED_PROTECTION=false check_component() { if [ -f "/opt/remnawave/caddy/docker-compose.yml" ] || [ -f "/opt/remnawave/caddy/Caddyfile" ]; then @@ -44,13 +45,101 @@ install_docker() { fi } +validate_password() { + local password="$1" + local prefix="$2" + + if [[ ${#password} -lt 8 ]]; then + warn "$(get_string "${prefix}_password_short")" + return 1 + fi + if ! [[ "$password" =~ [A-Z] ]]; then + warn "$(get_string "${prefix}_password_uppercase")" + return 1 + fi + if ! [[ "$password" =~ [a-z] ]]; then + warn "$(get_string "${prefix}_password_lowercase")" + return 1 + fi + if ! [[ "$password" =~ [0-9] ]]; then + warn "$(get_string "${prefix}_password_number")" + return 1 + fi + if ! [[ "$password" =~ [^a-zA-Z0-9] ]]; then + warn "$(get_string "${prefix}_password_special")" + return 1 + fi + return 0 +} + +request_protection_data() { + while true; do + question "$(get_string "install_caddy_need_protection")" + PROTECTION="$REPLY" + if [[ "$PROTECTION" == "y" || "$PROTECTION" == "Y" ]]; then + NEED_PROTECTION=true + break + elif [[ "$PROTECTION" == "n" || "$PROTECTION" == "N" ]]; then + NEED_PROTECTION=false + break + else + warn "$(get_string "install_caddy_please_enter_yn")" + fi + done + + if [[ "$NEED_PROTECTION" == true ]]; then + while true; do + question "$(get_string "install_caddy_enter_login_route")" + LOGIN_ROUTE="$REPLY" + if [[ -n "$LOGIN_ROUTE" ]]; then + LOGIN_ROUTE="${LOGIN_ROUTE#/}" + break + fi + warn "$(get_string "install_caddy_login_route_empty")" + done + + while true; do + question "$(get_string "install_caddy_enter_admin_login")" + ADMIN_LOGIN="$REPLY" + if [[ -n "$ADMIN_LOGIN" ]]; then + break + fi + warn "$(get_string "install_caddy_admin_login_empty")" + done + + while true; do + question "$(get_string "install_caddy_enter_admin_password")" + ADMIN_PASSWORD="$REPLY" + if validate_password "$ADMIN_PASSWORD" "install_caddy"; then + break + fi + done + fi +} + install_caddy() { if [ "$REINSTALL_CADDY" = true ]; then info "$(get_string "install_caddy_installing")" mkdir -p /opt/remnawave/caddy cd /opt/remnawave/caddy - cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + if [[ "$NEED_PROTECTION" == true ]]; then + cp "/opt/remnasetup/data/caddy/caddyfile-protected" Caddyfile + + ADMIN_PASSWORD_HASH=$(docker run --rm caddy:2.9 caddy hash-password --plaintext "$ADMIN_PASSWORD" 2>/dev/null) + if [[ -z "$ADMIN_PASSWORD_HASH" ]]; then + error "Failed to generate password hash. Falling back to standard config." + cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + NEED_PROTECTION=false + else + sed -i "s|\$LOGIN_ROUTE|$LOGIN_ROUTE|g" Caddyfile + sed -i "s|\$ADMIN_LOGIN|$ADMIN_LOGIN|g" Caddyfile + sed -i "s|\$ADMIN_PASSWORD_HASH|$ADMIN_PASSWORD_HASH|g" Caddyfile + fi + else + cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + fi + cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml if [[ -n "$PANEL_DOMAIN" ]]; then @@ -112,12 +201,33 @@ main() { SUB_PORT="" fi + if [[ -n "$PANEL_DOMAIN" ]]; then + request_protection_data + fi + if ! check_docker; then install_docker fi install_caddy success "$(get_string "install_caddy_complete")" + + if [[ "$NEED_PROTECTION" == true ]]; then + echo "" + echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" + if [ "$LANGUAGE" = "en" ]; then + echo -e "${BOLD_CYAN}Panel Protection Info${RESET}" + else + echo -e "${BOLD_CYAN}Информация о защите панели${RESET}" + fi + echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" + echo -e "${BOLD_GREEN}URL:${RESET} ${BLUE}https://${PANEL_DOMAIN}/${LOGIN_ROUTE}${RESET}" + echo -e "${BOLD_GREEN}Login:${RESET} ${BLUE}${ADMIN_LOGIN}${RESET}" + echo -e "${BOLD_GREEN}Password:${RESET} ${BLUE}${ADMIN_PASSWORD}${RESET}" + echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" + echo "" + fi + read -n 1 -s -r -p "$(get_string "install_caddy_press_key")" exit 0 } diff --git a/scripts/remnawave/install-full.sh b/scripts/remnawave/install-full.sh index 341d8c8..7b0e483 100644 --- a/scripts/remnawave/install-full.sh +++ b/scripts/remnawave/install-full.sh @@ -6,6 +6,7 @@ source "/opt/remnasetup/scripts/common/languages.sh" REINSTALL_PANEL=false REINSTALL_CADDY=false +NEED_PROTECTION=false check_component() { local component=$1 @@ -94,7 +95,77 @@ generate_login() { tr -dc 'a-zA-Z' < /dev/urandom | head -c 15 } +validate_password() { + local password="$1" + local prefix="$2" + if [[ ${#password} -lt 8 ]]; then + warn "$(get_string "${prefix}_password_short")" + return 1 + fi + if ! [[ "$password" =~ [A-Z] ]]; then + warn "$(get_string "${prefix}_password_uppercase")" + return 1 + fi + if ! [[ "$password" =~ [a-z] ]]; then + warn "$(get_string "${prefix}_password_lowercase")" + return 1 + fi + if ! [[ "$password" =~ [0-9] ]]; then + warn "$(get_string "${prefix}_password_number")" + return 1 + fi + if ! [[ "$password" =~ [^a-zA-Z0-9] ]]; then + warn "$(get_string "${prefix}_password_special")" + return 1 + fi + return 0 +} + +request_protection_data() { + while true; do + question "$(get_string "install_full_need_protection")" + PROTECTION="$REPLY" + if [[ "$PROTECTION" == "y" || "$PROTECTION" == "Y" ]]; then + NEED_PROTECTION=true + break + elif [[ "$PROTECTION" == "n" || "$PROTECTION" == "N" ]]; then + NEED_PROTECTION=false + break + else + warn "$(get_string "install_full_please_enter_yn")" + fi + done + + if [[ "$NEED_PROTECTION" == true ]]; then + while true; do + question "$(get_string "install_full_enter_login_route")" + LOGIN_ROUTE="$REPLY" + if [[ -n "$LOGIN_ROUTE" ]]; then + LOGIN_ROUTE="${LOGIN_ROUTE#/}" + break + fi + warn "$(get_string "install_full_login_route_empty")" + done + + while true; do + question "$(get_string "install_full_enter_admin_login")" + ADMIN_LOGIN="$REPLY" + if [[ -n "$ADMIN_LOGIN" ]]; then + break + fi + warn "$(get_string "install_full_admin_login_empty")" + done + + while true; do + question "$(get_string "install_full_enter_admin_password")" + ADMIN_PASSWORD="$REPLY" + if validate_password "$ADMIN_PASSWORD" "install_full"; then + break + fi + done + fi +} install_components() { if [ "$REINSTALL_PANEL" = true ]; then @@ -133,11 +204,31 @@ install_components() { fi if [ "$REINSTALL_CADDY" = true ]; then - info "$(get_string "install_full_installing_caddy")" + if [[ "$NEED_PROTECTION" == true ]]; then + info "$(get_string "install_full_installing_caddy_with_protection")" + else + info "$(get_string "install_full_installing_caddy")" + fi mkdir -p /opt/remnawave/caddy cd /opt/remnawave/caddy - cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + if [[ "$NEED_PROTECTION" == true ]]; then + cp "/opt/remnasetup/data/caddy/caddyfile-protected" Caddyfile + + ADMIN_PASSWORD_HASH=$(docker run --rm caddy:2.9 caddy hash-password --plaintext "$ADMIN_PASSWORD" 2>/dev/null) + if [[ -z "$ADMIN_PASSWORD_HASH" ]]; then + error "Failed to generate password hash. Falling back to standard config." + cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + NEED_PROTECTION=false + else + sed -i "s|\$LOGIN_ROUTE|$LOGIN_ROUTE|g" Caddyfile + sed -i "s|\$ADMIN_LOGIN|$ADMIN_LOGIN|g" Caddyfile + sed -i "s|\$ADMIN_PASSWORD_HASH|$ADMIN_PASSWORD_HASH|g" Caddyfile + fi + else + cp "/opt/remnasetup/data/caddy/caddyfile" Caddyfile + fi + cp "/opt/remnasetup/data/docker/caddy-compose.yml" docker-compose.yml sed -i "s|\$PANEL_DOMAIN|$PANEL_DOMAIN|g" Caddyfile @@ -165,7 +256,13 @@ show_panel_info() { echo -e "${BOLD_CYAN}$(get_string "install_full_panel_info_header")${RESET}" echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" - echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}${RESET}" + if [[ "$NEED_PROTECTION" == true ]]; then + echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}/${LOGIN_ROUTE}${RESET}" + echo -e "${BOLD_GREEN}Login:${RESET} ${BLUE}${ADMIN_LOGIN}${RESET}" + echo -e "${BOLD_GREEN}Password:${RESET} ${BLUE}${ADMIN_PASSWORD}${RESET}" + else + echo -e "${BOLD_GREEN}$(get_string "install_full_panel_url")${RESET} ${BLUE}https://${PANEL_DOMAIN}${RESET}" + fi echo -e "${MAGENTA}────────────────────────────────────────────────────────────${RESET}" @@ -204,6 +301,10 @@ main() { PANEL_PORT="$REPLY" PANEL_PORT=${PANEL_PORT:-3000} + if [ "$REINSTALL_CADDY" = true ]; then + request_protection_data + fi + if ! check_docker; then install_docker fi From 509f3d829f5e39cdc0b48e43c93e2c2279f2a2d2 Mon Sep 17 00:00:00 2001 From: ANHELL-dev Date: Mon, 23 Feb 2026 00:03:03 +0300 Subject: [PATCH 2/4] fix: use fork repo URL --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index b008ee2..f63444c 100644 --- a/install.sh +++ b/install.sh @@ -34,7 +34,7 @@ cd "$TEMP_DIR" || exit 1 echo "Downloading RemnaSetup..." echo "Загрузка RemnaSetup..." -curl -L https://github.com/Capybara-z/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip +curl -L https://github.com/ANHELL-dev/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip if [ ! -f remnasetup.zip ]; then echo "Error: Failed to download archive" From eeaab88831e932c1dda27651b128a0c19d7be3bb Mon Sep 17 00:00:00 2001 From: ANHELL-dev Date: Mon, 23 Feb 2026 00:09:58 +0300 Subject: [PATCH 3/4] Revert "fix: use fork repo URL" This reverts commit 509f3d829f5e39cdc0b48e43c93e2c2279f2a2d2. :wq# --- install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.sh b/install.sh index f63444c..b008ee2 100644 --- a/install.sh +++ b/install.sh @@ -34,7 +34,7 @@ cd "$TEMP_DIR" || exit 1 echo "Downloading RemnaSetup..." echo "Загрузка RemnaSetup..." -curl -L https://github.com/ANHELL-dev/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip +curl -L https://github.com/Capybara-z/RemnaSetup/archive/refs/heads/main.zip -o remnasetup.zip if [ ! -f remnasetup.zip ]; then echo "Error: Failed to download archive" From ba30d114411886f044ffb8d41705574f05e5f035 Mon Sep 17 00:00:00 2001 From: ANHELL-dev Date: Mon, 23 Feb 2026 00:32:24 +0300 Subject: [PATCH 4/4] fix: basicauth inside handle block --- data/caddy/caddyfile-protected | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/data/caddy/caddyfile-protected b/data/caddy/caddyfile-protected index 141ed54..549b5c6 100644 --- a/data/caddy/caddyfile-protected +++ b/data/caddy/caddyfile-protected @@ -3,17 +3,12 @@ https://$PANEL_DOMAIN { reverse_proxy http://remnawave:$PANEL_PORT } - handle /$LOGIN_ROUTE* { + handle { basicauth { $ADMIN_LOGIN $ADMIN_PASSWORD_HASH } - uri strip_prefix /$LOGIN_ROUTE reverse_proxy http://remnawave:$PANEL_PORT } - - handle { - respond 404 - } } :443 {