From 91df8f5a2600976333f0a679473a7f3cf109439a Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Mon, 23 Feb 2026 19:49:12 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20install.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install.sh | 508 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 install.sh diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..82f62d6 --- /dev/null +++ b/install.sh @@ -0,0 +1,508 @@ +#!/bin/bash + +export LANG=ru_RU.UTF-8 + +RED="\033[31m" +GREEN="\033[32m" +YELLOW="\033[33m" +PLAIN="\033[0m" + +red(){ + echo -e "\033[31m\033[01m$1\033[0m" +} + +green(){ + echo -e "\033[32m\033[01m$1\033[0m" +} + +yellow(){ + echo -e "\033[33m\033[01m$1\033[0m" +} + +if [[ $EUID -ne 0 ]]; then + red "Внимание: Запустите скрипт от имени root пользователя" + exit 1 +fi + +CUSTOM_SNI="" +for arg in "$@"; do + case $arg in + --custom-sni=*) + CUSTOM_SNI="${arg#*=}" + shift + ;; + --help) + echo "Использование: $0 [--custom-sni=example.com]" + echo "" + echo "Опции:" + echo " --custom-sni=HOSTNAME Указать свой SNI хост (по умолчанию: web.max.ru)" + echo " --help Показать эту справку" + exit 0 + ;; + esac +done + +REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "'amazon linux'" "fedora" "alpine") +RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Fedora" "Alpine") +PACKAGE_UPDATE=("apt-get update" "apt-get update" "yum -y update" "yum -y update" "yum -y update" "apk update -f") +PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "yum -y install" "yum -y install" "apk add -f") + +CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" "$(lsb_release -sd 2>/dev/null)" "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" "$(grep . /etc/redhat-release 2>/dev/null)" "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')") + +for i in "${CMD[@]}"; do + SYS="$i" && [[ -n $SYS ]] && break +done + +for ((int = 0; int < ${#REGEX[@]}; int++)); do + [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && [[ -n $SYSTEM ]] && break +done + +[[ -z $SYSTEM ]] && red "Текущая система VPS не поддерживается, используйте основную операционную систему" && exit 1 + +if [[ -z $(type -P curl) ]]; then + if [[ ! $SYSTEM == "CentOS" ]]; then + ${PACKAGE_UPDATE[int]} + fi + ${PACKAGE_INSTALL[int]} curl +fi + +get_ip() { + local ip=$(curl -s4m8 ip.sb -k) || ip=$(curl -s6m8 ip.sb -k) + echo "$ip" +} + +install_server_core() { + yellow "Установка Hysteria2..." + + set -e + + SCRIPT_ARGS=("$@") + + EXECUTABLE_INSTALL_PATH="/usr/local/bin/hysteria" + + SYSTEMD_SERVICES_DIR="/etc/systemd/system" + + CONFIG_DIR="/etc/hysteria" + + REPO_URL="https://github.com/apernet/hysteria" + + HY2_API_BASE_URL="https://api.hy2.io/v1" + + CURL_FLAGS=(-L -f -q --retry 5 --retry-delay 10 --retry-max-time 60) + + PACKAGE_MANAGEMENT_INSTALL="${PACKAGE_MANAGEMENT_INSTALL:-}" + + OPERATING_SYSTEM="${OPERATING_SYSTEM:-}" + + ARCHITECTURE="${ARCHITECTURE:-}" + + HYSTERIA_USER="${HYSTERIA_USER:-}" + + HYSTERIA_HOME_DIR="${HYSTERIA_HOME_DIR:-}" + + OPERATION= + + VERSION= + + FORCE= + + LOCAL_FILE= + + has_command() { + local _command=$1 + type -P "$_command" > /dev/null 2>&1 + } + + curl() { + command curl "${CURL_FLAGS[@]}" "$@" + } + + mktemp() { + command mktemp "$@" "/tmp/hyservinst.XXXXXXXXXX" + } + + note() { + local _msg="$1" + echo -e "$SCRIPT_NAME: $(tput bold)note: $_msg$(tput sgr0)" + } + + warning() { + local _msg="$1" + echo -e "$SCRIPT_NAME: $(tput setaf 3)warning: $_msg$(tput sgr0)" + } + + error() { + local _msg="$1" + echo -e "$SCRIPT_NAME: $(tput setaf 1)error: $_msg$(tput sgr0)" + } + + check_environment_operating_system() { + if [[ -n "$OPERATING_SYSTEM" ]]; then + warning "OPERATING_SYSTEM=$OPERATING_SYSTEM обнаружено, определение ОС выполняться не будет." + return + fi + + if [[ "x$(uname)" == "xLinux" ]]; then + OPERATING_SYSTEM=linux + return + fi + + error "Этот скрипт поддерживает только Linux." + exit 95 + } + + check_environment_architecture() { + if [[ -n "$ARCHITECTURE" ]]; then + warning "ARCHITECTURE=$ARCHITECTURE обнаружено, определение архитектуры выполняться не будет." + return + fi + + case "$(uname -m)" in + 'i386' | 'i686') + ARCHITECTURE='386' + ;; + 'amd64' | 'x86_64') + ARCHITECTURE='amd64' + ;; + 'armv5tel' | 'armv6l' | 'armv7' | 'armv7l') + ARCHITECTURE='arm' + ;; + 'armv8' | 'aarch64') + ARCHITECTURE='arm64' + ;; + 'mips' | 'mipsle' | 'mips64' | 'mips64le') + ARCHITECTURE='mipsle' + ;; + 's390x') + ARCHITECTURE='s390x' + ;; + *) + error "Архитектура '$(uname -a)' не поддерживается." + exit 8 + ;; + esac + } + + check_environment_systemd() { + if [[ -d "/run/systemd/system" ]] || grep -q systemd <(ls -l /sbin/init); then + return + fi + + case "$FORCE_NO_SYSTEMD" in + '1') + warning "FORCE_NO_SYSTEMD=1, продолжим даже если systemd не обнаружен." + ;; + '2') + warning "FORCE_NO_SYSTEMD=2, продолжим но пропустим все команды связанные с systemd." + ;; + *) + error "Этот скрипт поддерживает только дистрибутивы Linux с systemd." + exit 1 + ;; + esac + } + + update_packages() { + ${PACKAGE_UPDATE[int]} + } + + check_environment_curl() { + if has_command curl; then + return + fi + ${PACKAGE_INSTALL[int]} curl + } + + check_environment_grep() { + if has_command grep; then + return + fi + ${PACKAGE_INSTALL[int]} grep + } + + check_environment_qrencode() { + if has_command qrencode; then + return + fi + ${PACKAGE_INSTALL[int]} qrencode + } + + check_environment() { + update_packages + check_environment_operating_system + check_environment_architecture + check_environment_systemd + check_environment_curl + check_environment_grep + check_environment_qrencode + } + + install_content() { + local _install_flags="$1" + local _content="$2" + local _destination="$3" + local _overwrite="$4" + + local _tmpfile="$(mktemp)" + + echo -ne "Установка $_destination ... " + echo "$_content" > "$_tmpfile" + if [[ -z "$_overwrite" && -e "$_destination" ]]; then + echo -e "существует" + elif install "$_install_flags" "$_tmpfile" "$_destination"; then + echo -e "ок" + fi + + rm -f "$_tmpfile" + } + + get_latest_version() { + if [[ -n "$VERSION" ]]; then + echo "$VERSION" + return + fi + + local _tmpfile=$(mktemp) + if ! curl -sS "$HY2_API_BASE_URL/update?cver=installscript&plat=${OPERATING_SYSTEM}&arch=${ARCHITECTURE}&chan=release&side=server" -o "$_tmpfile"; then + error "Ошибка получения последней версии от Hysteria 2 API" + exit 11 + fi + + local _latest_version=$(grep -oP '"lver":\s*\K"v.*?"' "$_tmpfile" | head -1) + _latest_version=${_latest_version#'"'} + _latest_version=${_latest_version%'"'} + + if [[ -n "$_latest_version" ]]; then + echo "$_latest_version" + fi + + rm -f "$_tmpfile" + } + + download_hysteria() { + local _version="$1" + local _destination="$2" + + local _download_url="$REPO_URL/releases/download/app/$_version/hysteria-$OPERATING_SYSTEM-$ARCHITECTURE" + echo "Загрузка бинарного файла hysteria: $_download_url ..." + if ! curl -R -H 'Cache-Control: no-cache' "$_download_url" -o "$_destination"; then + error "Ошибка загрузки, проверьте ваше соединение и попробуйте снова." + return 11 + fi + return 0 + } + + perform_install_hysteria_binary() { + local _tmpfile=$(mktemp) + local _version=$(get_latest_version) + + if ! download_hysteria "$_version" "$_tmpfile"; then + rm -f "$_tmpfile" + exit 11 + fi + + echo -ne "Установка исполняемого файла hysteria ... " + if install -Dm755 "$_tmpfile" "$EXECUTABLE_INSTALL_PATH"; then + echo "ок" + else + exit 13 + fi + + rm -f "$_tmpfile" + + mkdir -p /etc/hysteria + } + + perform_install_hysteria_systemd() { + if [[ "x$FORCE_NO_SYSTEMD" == "x2" ]]; then + return + fi + + local _service_content=$(cat << 'EOF' +[Unit] +Description=Hysteria Server Service (config.yaml) +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/hysteria server --config /etc/hysteria/config.yaml +WorkingDirectory=~ +User=root +Group=root +Environment=HYSTERIA_LOG_LEVEL=info +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW +NoNewPrivileges=true + +[Install] +WantedBy=multi-user.target +EOF +) + install_content -Dm644 "$_service_content" "$SYSTEMD_SERVICES_DIR/hysteria-server.service" "1" + + systemctl daemon-reload + } + + check_environment + HYSTERIA_USER="root" + HYSTERIA_HOME_DIR="/root" + + perform_install_hysteria_binary + perform_install_hysteria_systemd + + green "Hysteria2 core успешно установлен!" +} + +configure_hysteria() { + yellow "Настройка сервера Hysteria2..." + + if [[ -n "$CUSTOM_SNI" ]]; then + local sni_host="$CUSTOM_SNI" + yellow "Используется кастомный SNI: $sni_host" + else + local sni_host="web.max.ru" + fi + + local masquerade_url="$sni_host" + local port="443" + + mkdir -p /etc/hysteria + + local auth_pwd=$(date +%s%N | md5sum | cut -c 1-16) + local obfs_pwd=$(date +%s%N | md5sum | cut -c 1-16) + + openssl ecparam -genkey -name prime256v1 -out /etc/hysteria/private.key + openssl req -new -x509 -days 36500 -key /etc/hysteria/private.key -out /etc/hysteria/cert.crt -subj "/CN=$sni_host" + chmod 600 /etc/hysteria/cert.crt + chmod 600 /etc/hysteria/private.key + + cat << EOF > /etc/hysteria/config.yaml +listen: :$port + +tls: + cert: /etc/hysteria/cert.crt + key: /etc/hysteria/private.key + +obfs: + type: salamander + salamander: + password: $obfs_pwd + +auth: + type: password + password: $auth_pwd + +masquerade: + type: proxy + proxy: + url: https://$masquerade_url + rewriteHost: true + +quic: + initStreamReceiveWindow: 16777216 + maxStreamReceiveWindow: 16777216 + initConnReceiveWindow: 33554432 + maxConnReceiveWindow: 33554432 +EOF + + local server_ip=$(get_ip) + + cat << EOF > /root/hysteria2.txt +hy2://$auth_pwd@$server_ip:$port?mport&security=tls&sni=$sni_host&allowInsecure=true&alpn&obfs=salamander&obfs-password=$obfs_pwd#Test +EOF + + green "Настройка завершена!" + echo + yellow "IP сервера: $server_ip" + yellow "Порт: $port" + yellow "SNI: $sni_host" + yellow "Пароль аутентификации: $auth_pwd" + yellow "Пароль обфускации: $obfs_pwd" + yellow "Маскировка: https://$masquerade_url" + echo + + if [[ -n "$CUSTOM_SNI" ]]; then + green "Использован кастомный SNI: $CUSTOM_SNI" + fi +} + +start_service() { + yellow "Запуск службы Hysteria2..." + + systemctl daemon-reload + systemctl enable hysteria-server + systemctl start hysteria-server + + sleep 2 + if systemctl is-active --quiet hysteria-server; then + green "Служба Hysteria2 успешно запущена" + else + red "Ошибка запуска службы Hysteria2" + systemctl status hysteria-server + exit 1 + fi +} + +show_config() { + if command -v qrencode &> /dev/null; then + green "=== QR Code ===" + qrencode -t ANSIUTF8 "$(cat /root/hysteria2.txt)" + else + yellow "Установите qrencode для генерации QR кода: apt install qrencode / yum install qrencode" + fi +} + +uninstall_hysteria() { + red "Удаление Hysteria2..." + + systemctl stop hysteria-server 2>/dev/null || true + systemctl disable hysteria-server 2>/dev/null || true + rm -f /etc/systemd/system/hysteria-server.service + rm -f /usr/local/bin/hysteria + rm -rf /etc/hysteria + rm -f /root/hysteria2.txt + systemctl daemon-reload + + green "Hysteria2 полностью удален!" +} + +check_hysteria_installed() { + [[ -f "/usr/local/bin/hysteria" ]] +} + +main() { + if check_hysteria_installed; then + red "Hysteria2 уже установлен!" + echo + read -p "Хотите переустановить? [y/N]: " reinstall + case $reinstall in + [yY]|[yY][eE][sS]) + uninstall_hysteria + ;; + *) + echo "Выход..." + exit 0 + ;; + esac + fi + + install_server_core + configure_hysteria + start_service + show_config + + echo + green "Установка Hysteria2 успешно завершена!" + echo + yellow "Конфиг клиента: /root/hysteria2.txt" + yellow "Перезапуск службы: systemctl restart hysteria-server" + yellow "Проверка статуса: systemctl status hysteria-server" + echo + yellow "Hysteria2 ключ:" + cat /root/hysteria2.txt + echo + echo "Инструкции по настройке VPN приложений:" + echo "https://github.com/YukiKras/wiki/blob/main/nastroikavpn.md" +} + +main \ No newline at end of file