diff --git a/reality-ezpz.sh b/reality-ezpz.sh index 956137d..215b198 100644 --- a/reality-ezpz.sh +++ b/reality-ezpz.sh @@ -38,13 +38,13 @@ HEIGHT=30 WIDTH=60 CHOICE_HEIGHT=20 -image[xray]="teddysun/xray:1.8.4" -image[sing-box]="gzxhwq/sing-box:v1.6.5" -image[nginx]="nginx:1.24.0" -image[certbot]="certbot/certbot:v2.6.0" -image[haproxy]="haproxy:2.8.0" -image[python]="python:3.11-alpine" -image[wgcf]="virb3/wgcf:2.2.18" +image[xray]="teddysun/xray:1.8.23" +image[sing-box]="gzxhwq/sing-box:1.9.3" +image[nginx]="nginx:1.27.0" +image[certbot]="certbot/certbot:v2.11.0" +image[haproxy]="haproxy:2.9.9" +image[python]="python:3.12-alpine" +image[wgcf]="virb3/wgcf:2.2.22" defaults[transport]=tcp defaults[domain]=www.google.com @@ -104,9 +104,9 @@ regex[url]="^(http|https)://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|[0-9]{1,3}(\.[0-9]{1,3 function show_help { echo "" echo "Usage: reality-ezpz.sh [-t|--transport=tcp|http|grpc|ws|tuic|hysteria2] [-d|--domain=] [--server=] [--regenerate] [--default] - [-r|--restart] [--enable-safenet=true|false] [--port=] [-c|--core=xray|sing-box] - [--enable-warp=true|false] [--warp-license=] [--security=reality|letsencrypt|selfsigned] [-m|--menu] [--show-server-config] - [--add-user=] [--lists-users] [--show-user=] [--delete-user=] [--backup] [--restore=] [-u|--uninstall]" + [-r|--restart] [--enable-safenet=true|false] [--port=] [-c|--core=xray|sing-box] [--enable-warp=true|false] + [--warp-license=] [--security=reality|letsencrypt|selfsigned] [-m|--menu] [--show-server-config] [--add-user=] [--lists-users] + [--show-user=] [--delete-user=] [--backup] [--restore=] [--backup-password=] [-u|--uninstall]" echo "" echo " -t, --transport Transport protocol (tcp, http, grpc, ws, tuic, hysteria2, default: ${defaults[transport]})" echo " -d, --domain Domain to use as SNI (default: ${defaults[domain]})" @@ -130,15 +130,16 @@ function show_help { echo " --list-users List all users" echo " --show-user Shows the config and QR code of the user" echo " --delete-user Delete the user" - echo " --backup Backup users and configuration and upload it to keep.sh" + echo " --backup Backup users and configuration and upload it to temp.sh" echo " --restore Restore backup from URL or file" + echo " --backup-password Create/Restore password protected backup file" echo " -h, --help Display this help message" return 1 } function parse_args { local opts - opts=$(getopt -o t:d:ruc:mh --long transport:,domain:,server:,regenerate,default,restart,uninstall,enable-safenet:,port:,warp-license:,enable-warp:,core:,security:,menu,show-server-config,add-user:,list-users,show-user:,delete-user:,backup,restore:,enable-tgbot:,tgbot-token:,tgbot-admins:,help -- "$@") + opts=$(getopt -o t:d:ruc:mh --long transport:,domain:,server:,regenerate,default,restart,uninstall,enable-safenet:,port:,warp-license:,enable-warp:,core:,security:,menu,show-server-config,add-user:,list-users,show-user:,delete-user:,backup,restore:,backup-password:,enable-tgbot:,tgbot-token:,tgbot-admins:,help -- "$@") if [[ $? -ne 0 ]]; then return 1 fi @@ -278,7 +279,7 @@ function parse_args { echo "Invalid Telegram Bot Token: ${args[tgbot_token]}" return 1 fi - if ! curl -sSfL "https://api.telegram.org/bot${args[tgbot_token]}/getMe" >/dev/null 2>&1; then + if ! curl -sSfL -m 3 "https://api.telegram.org/bot${args[tgbot_token]}/getMe" >/dev/null 2>&1; then echo "Invalid Telegram Bot Token: Telegram Bot Token is incorrect. Check it again." return 1 fi @@ -328,6 +329,10 @@ function parse_args { fi shift 2 ;; + --backup-password) + args[backup_password]="$2" + shift 2 + ;; -h|--help) return 1 ;; @@ -353,40 +358,83 @@ function parse_args { function backup { local backup_name + local backup_password="$1" local backup_file_url local exit_code - backup_name=reality-ezpz-backup-$(date +%Y-%m-%d_%H-%M-%S).tar.gz - tar -czf "/tmp/${backup_name}" -C "${config_path}" ./ - if ! backup_file_url=$(curl -fsS --upload-file "/tmp/${backup_name}" https://free.keep.sh); then + backup_name="reality-ezpz-backup-$(date +%Y-%m-%d_%H-%M-%S).zip" + cd "${config_path}" + if [ -z "${backup_password}" ]; then + zip -r "/tmp/${backup_name}" . > /dev/null + else + zip -P "${backup_password}" -r "/tmp/${backup_name}" . > /dev/null + fi + if ! backup_file_url=$(curl -fsS -m 30 -F "file=@/tmp/${backup_name}" "https://temp.sh/upload"); then rm -f "/tmp/${backup_name}" echo "Error in uploading backup file" >&2 return 1 fi rm -f "/tmp/${backup_name}" echo "${backup_file_url}" - return } function restore { - local backup_file=$1 + local backup_file="$1" + local backup_password="$2" local temp_file + local unzip_output + local unzip_exit_code + local current_state if [[ ! -r ${backup_file} ]]; then temp_file=$(mktemp -u) - if ! curl -fSsL "${backup_file}" -o "${temp_file}"; then - echo "Cannot download or find backup file" >&2 - return 1 + if [[ "${backup_file}" =~ ^https?://temp\.sh/ ]]; then + if ! curl -fSsL -m 30 -X POST "${backup_file}" -o "${temp_file}"; then + echo "Cannot download or find backup file" >&2 + return 1 + fi + else + if ! curl -fSsL -m 30 "${backup_file}" -o "${temp_file}"; then + echo "Cannot download or find backup file" >&2 + return 1 + fi fi backup_file="${temp_file}" fi - if ! tar -tzf "${backup_file}" | grep -q config; then - echo "The provided file is not a reality-ezpz backup file." >&2 + current_state=$(set +o) + set +e + if [[ -z "${backup_password}" ]]; then + unzip_output=$(unzip -P "" -t "${backup_file}" 2>&1) + else + unzip_output=$(unzip -P "${backup_password}" -t "${backup_file}" 2>&1) + fi + unzip_exit_code=$? + eval "$current_state" + if [[ ${unzip_exit_code} -eq 0 ]]; then + if ! echo "${unzip_output}" | grep -q 'config'; then + echo "The provided file is not a reality-ezpz backup file." >&2 + rm -f "${temp_file}" + return 1 + fi + else + if echo "${unzip_output}" | grep -q 'incorrect password'; then + echo "The provided password for backup file is incorrect." >&2 + else + echo "An error occurred during zip file verification: ${unzip_output}" >&2 + fi rm -f "${temp_file}" return 1 fi rm -rf "${config_path}" mkdir -p "${config_path}" - if ! tar -xzf "${backup_file}" -C "${config_path}"; then - echo "Error in backup restore." >&2 + set +e + if [[ -z "${backup_password}" ]]; then + unzip_output=$(unzip -d "${config_path}" "${backup_file}" 2>&1) + else + unzip_output=$(unzip -P "${backup_password}" -d "${config_path}" "${backup_file}" 2>&1) + fi + unzip_exit_code=$? + eval "$current_state" + if [[ ${unzip_exit_code} -ne 0 ]]; then + echo "Error in backup restore: ${unzip_output}" >&2 rm -f "${temp_file}" return 1 fi @@ -636,16 +684,19 @@ function uninstall { } function install_packages { - if ! which qrencode whiptail jq xxd >/dev/null 2>&1; then + if [[ -n $BOT_TOKEN ]]; then + return 0 + fi + if ! which qrencode whiptail jq xxd zip unzip >/dev/null 2>&1; then if which apt >/dev/null 2>&1; then apt update - apt install qrencode whiptail jq xxd -y + DEBIAN_FRONTEND=noninteractive apt install qrencode whiptail jq xxd zip unzip -y return 0 fi if which yum >/dev/null 2>&1; then yum makecache yum install epel-release -y || true - yum install qrencode newt jq vim-common -y + yum install qrencode newt jq vim-common zip unzip -y return 0 fi echo "OS is not supported!" @@ -655,7 +706,7 @@ function install_packages { function install_docker { if ! which docker >/dev/null 2>&1; then - curl -fsSL https://get.docker.com | bash + curl -fsSL -m 5 https://get.docker.com | bash systemctl enable --now docker docker_cmd="docker compose" return 0 @@ -668,7 +719,7 @@ function install_docker { docker_cmd="docker-compose" return 0 fi - curl -fsSL https://github.com/docker/compose/releases/download/v2.17.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose + curl -fsSL -m 30 https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-linux-$(uname -m) -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose docker_cmd="docker-compose" return 0 @@ -696,7 +747,7 @@ services: $([[ ${config[security]} != 'reality' ]] && echo "- 8443" || true) restart: always environment: - TZ: Etc/UTC + TZ: Asia/Singapore volumes: - ./${path[engine]#${config_path}/}:/etc/${config[core]}/config.json $([[ ${config[security]} != 'reality' ]] && { [[ ${config[transport]} == 'http' ]] || [[ ${config[transport]} == 'tcp' ]] || [[ ${config[transport]} == 'tuic' ]] || [[ ${config[transport]} == 'hysteria2' ]]; } && echo "- ./${path[server_crt]#${config_path}/}:/etc/${config[core]}/server.crt" || true) @@ -710,6 +761,8 @@ echo " expose: - 80 restart: always + volumes: + - ./website:/usr/share/nginx/html networks: - reality haproxy: @@ -738,6 +791,7 @@ echo " - ./$(dirname "${path[server_pem]#${config_path}/}"):/certificate - ./${path[certbot_deployhook]#${config_path}/}:/deployhook.sh - ./${path[certbot_startup]#${config_path}/}:/startup.sh + - ./website:/website networks: - reality entrypoint: /bin/sh @@ -777,18 +831,15 @@ echo " global ssl-default-bind-options ssl-min-ver TLSv1.2 defaults - retries 3 option http-server-close timeout connect 5s - timeout client 5s + timeout client 50s timeout client-fin 1s timeout server-fin 1s - timeout server 5s - timeout tunnel 300s + timeout server 50s + timeout tunnel 50s timeout http-keep-alive 1s - timeout http-request 5s timeout queue 15s - timeout tarpit 5s frontend http mode http bind :::8080 v4v6 @@ -859,13 +910,17 @@ awk -F= '{print \$2}' | tr -d : | tr '[:upper:]' '[:lower:]') fi fi while true; do - response=\$(curl -skL --max-time 3 http://${config[server]}) - if echo "\$response" | grep 'Welcome to nginx!' >/dev/null; then + ls -d /website/* | grep -E '^/website/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\$'|xargs rm -f + uuid=\$(uuidgen) + echo "\$uuid" > "/website/\$uuid" + response=\$(curl -skL --max-time 3 http://${config[server]}/\$uuid) + if echo "\$response" | grep \$uuid >/dev/null; then break fi echo "Domain ${config[server]} is not pointing to the server" sleep 5 done +ls -d /website/* | grep -E '^/website/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\$'|xargs rm -f while true; do certbot certonly -n \\ --standalone \\ @@ -904,7 +959,7 @@ EOF function generate_certbot_dockerfile { cat >"${path[certbot_dockerfile]}" << EOF FROM ${image[certbot]} -RUN apk add --no-cache docker-cli-compose curl +RUN apk add --no-cache docker-cli-compose curl uuidgen EOF } @@ -912,14 +967,14 @@ function generate_tgbot_dockerfile { cat >"${path[tgbot_dockerfile]}" << EOF FROM ${image[python]} WORKDIR ${config_path}/tgbot -RUN apk add --no-cache docker-cli-compose curl bash newt libqrencode-tools sudo openssl jq +RUN apk add --no-cache docker-cli-compose curl bash newt libqrencode-tools sudo openssl jq zip unzip RUN pip install --no-cache-dir python-telegram-bot==13.5 CMD [ "python", "./tgbot.py" ] EOF } function download_tgbot_script { - curl -fsSL https://raw.githubusercontent.com/aleskxyz/reality-ezpz/master/tgbot.py -o "${path[tgbot_script]}" + curl -fsSL -m 3 https://raw.githubusercontent.com/aleskxyz/reality-ezpz/master/tgbot.py -o "${path[tgbot_script]}" } function generate_selfsigned_certificate { @@ -972,6 +1027,7 @@ function generate_engine_config { if [[ ${config[warp]} == 'ON' ]]; then warp_object='{ "type": "wireguard", + "tag": "warp", "server": "engage.cloudflareclient.com", "server_port": 2408, "system_interface": false, @@ -1005,7 +1061,7 @@ function generate_engine_config { }, "dns": { "servers": [ - $([[ ${config[safenet]} == ON ]] && echo '{"address": "tcp://1.1.1.3", "detour": "dns"},{"address": "tcp://1.0.0.3", "detour": "dns"}' || echo '{"address": "tcp://1.1.1.1", "detour": "dns"},{"address": "tcp://1.0.0.1", "detour": "dns"}') + $([[ ${config[safenet]} == ON ]] && echo '{"address": "tcp://1.1.1.3", "detour": "internet"},{"address": "tcp://1.0.0.3", "detour": "internet"}' || echo '{"address": "tcp://1.1.1.1", "detour": "internet"},{"address": "tcp://1.0.0.1", "detour": "internet"}') ], "strategy": "prefer_ipv4" }, @@ -1052,17 +1108,18 @@ function generate_engine_config { } ], "outbounds": [ - $([[ ${config[warp]} == ON ]] && echo "${warp_object}" || echo '{"type": "direct"},') { "type": "direct", - "tag": "dns" + "tag": "internet" }, + $([[ ${config[warp]} == ON ]] && echo "${warp_object}" || true) { "type": "block", "tag": "block" } ], "route": { + "final": "$([[ ${config[warp]} == ON ]] && echo "warp" || echo "internet")", "rules": [ { "geoip": [ @@ -1143,6 +1200,7 @@ EOF if [[ ${config[warp]} == 'ON' ]]; then warp_object='{ "protocol": "wireguard", + "tag": "warp", "settings": { "secretKey": "'"${config[warp_private_key]}"'", "address": [ @@ -1171,7 +1229,7 @@ EOF "loglevel": "error" }, "dns": { - "servers": [$([[ ${config[safenet]} == ON ]] && echo '"tcp+local://1.1.1.3","tcp+local://1.0.0.3"' || echo '"tcp+local://1.1.1.1","tcp+local://1.0.0.1"')] + "servers": [$([[ ${config[safenet]} == ON ]] && echo '"https+local://family.cloudflare-dns.com/dns-query","tcp+local://1.1.1.3"' || echo '"https+local://dns.cloudflare.com/dns-query","tcp+local://1.1.1.1"')] }, "inbounds": [ { @@ -1188,6 +1246,7 @@ EOF "listen": "0.0.0.0", "port": 8443, "protocol": "vless", + "tag": "inbound", "settings": { "clients": [${users_object}], "decryption": "none" @@ -1209,13 +1268,18 @@ EOF "enabled": true, "destOverride": [ "http", - "tls" + "tls", + "quic" ] } } ], "outbounds": [ - $([[ ${config[warp]} == ON ]] && echo "${warp_object}" || echo '{"protocol": "freedom"},') + { + "protocol": "freedom", + "tag": "internet" + }, + $([[ ${config[warp]} == ON ]] && echo "${warp_object}" || true) { "protocol": "blackhole", "tag": "block" @@ -1268,6 +1332,11 @@ EOF "domain:sunlight-leds.com", "domain:icecyber.org" ] + }, + { + "type": "field", + "inboundTag": "inbound", + "outboundTag": "$([[ ${config[warp]} == ON ]] && echo "warp" || echo "internet")" } ] }, @@ -1318,7 +1387,7 @@ function generate_config { } function get_ipv6 { - curl -fsSL --ipv6 https://cloudflare.com/cdn-cgi/trace 2> /dev/null | grep ip | cut -d '=' -f2 + curl -fsSL -m 3 --ipv6 https://cloudflare.com/cdn-cgi/trace 2> /dev/null | grep ip | cut -d '=' -f2 } function print_client_configuration { @@ -1599,6 +1668,7 @@ $([[ ${config[security]} == 'reality' ]] && echo "ShortId: ${config[short_id]}" echo echo "Press Enter to return ..." read + clear fi if [[ $# -gt 0 ]]; then return 0 @@ -2069,7 +2139,7 @@ function config_tgbot_menu { message_box "Invalid Input" "Invalid Telegram Bot Token" continue fi - if ! curl -sSfL "https://api.telegram.org/bot${tgbot_token}/getMe" >/dev/null 2>&1; then + if ! curl -sSfL -m 3 "https://api.telegram.org/bot${tgbot_token}/getMe" >/dev/null 2>&1; then message_box "Invalid Input" "Telegram Bot Token is incorrect. Check it again." continue fi @@ -2097,26 +2167,30 @@ function config_tgbot_menu { } function backup_menu { + local backup_password local result - whiptail \ + backup_password=$(whiptail \ --clear \ --backtitle "$BACKTITLE" \ --title "Backup" \ - --yesno "Do you want to create a backup from users and configuration?" \ + --inputbox "Choose a password for the backup file.\nLeave blank if you do not wish to set a password for the backup file." \ $HEIGHT $WIDTH \ - 3>&1 1>&2 2>&3 + 3>&1 1>&2 2>&3) if [[ $? -ne 0 ]]; then return fi - if result=$(backup 2>&1); then + if result=$(backup "${backup_password}" 2>&1); then + clear echo "Backup has been create and uploaded successfully." echo "You can download the backup file from here:" + echo "" echo "${result}" echo "" - echo "The URL is valid for 24h." + echo "The URL is valid for 3 days." echo echo "Press Enter to return ..." read + clear else message_box "Backup Failed" "${result}" fi @@ -2124,6 +2198,7 @@ function backup_menu { function restore_backup_menu { local backup_file + local backup_password local result while true; do backup_file=$(whiptail \ @@ -2140,13 +2215,24 @@ function restore_backup_menu { message_box "Invalid Backup path of URL" "Backup file path or URL is not valid." continue fi - if result=$(restore ${backup_file} 2>&1); then + backup_password=$(whiptail \ + --clear \ + --backtitle "$BACKTITLE" \ + --title "Restore Backup" \ + --inputbox "Enter backup file password.\nLeave blank if there is no password." \ + $HEIGHT $WIDTH \ + 3>&1 1>&2 2>&3) + if [[ $? -ne 0 ]]; then + continue + fi + if result=$(restore "${backup_file}" "${backup_password}" 2>&1); then parse_config_file parse_users_file build_config update_config_file update_users_file message_box "Backup Restore Successful" "Backup has been restored successfully." + args[restart]=true break else message_box "Backup Restore Failed" "${result}" @@ -2196,7 +2282,7 @@ function warp_api { if [[ -n ${team_token} ]]; then headers+=("Cf-Access-Jwt-Assertion: ${team_token}") fi - command="curl -sLX ${verb} -w '%{http_code}' -o ${temp_file} ${endpoint}${resource}" + command="curl -sLX ${verb} -m 3 -w '%{http_code}' -o ${temp_file} ${endpoint}${resource}" for header in "${headers[@]}"; do command+=" -H '${header}'" done @@ -2419,17 +2505,31 @@ if [[ $EUID -ne 0 ]]; then exit 1 fi if [[ ${args[backup]} == true ]]; then - if backup_url=$(backup); then + if [[ -n ${args[backup_password]} ]]; then + backup_url=$(backup "${args[backup_password]}") + else + backup_url=$(backup) + fi + if [[ $? -eq 0 ]]; then echo "Backup created successfully. You can download the backup file from this address:" echo "${backup_url}" - echo "The URL is valid for 24h." + echo "The URL is valid for 3 days." + exit 0 fi fi if [[ -n ${args[restore]} ]]; then - if restore ${args[restore]}; then + if [[ -n ${args[backup_password]} ]]; then + restore "${args[restore]}" "${args[backup_password]}" + else + restore "${args[restore]}" + fi + if [[ $? -eq 0 ]]; then args[restart]=true echo "Backup has been restored successfully." fi + echo "Press Enter to continue ..." + read + clear fi generate_file_list install_packages