From feaa85a39b9996ebf664201c2bb0c23680b141c7 Mon Sep 17 00:00:00 2001 From: Ochenstarik Date: Tue, 14 Jul 2026 13:20:04 +0700 Subject: [PATCH] Add interactive SSH port and UFW modes --- README.md | 21 ++++-- ochenstarik-server-user-3.sh | 128 +++++++++++++++++++++++++++++++++-- 2 files changed, 141 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 38d3922..811f0bd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ | --- | --- | --- | | `ochenstarik-server-1.sh` | Создаёт swap-файл 2 ГБ и задаёт `vm.swappiness=20` | Да | | `ochenstarik-server-2.sh` | Обновляет систему, ставит серверные и офисные пакеты, настраивает часовой пояс и UFW | Да | -| `ochenstarik-server-user-3.sh` | Создаёт администратора, устанавливает SSH-ключ, переносит SSH на порт 20202, включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | +| `ochenstarik-server-user-3.sh` | Создаёт администратора, позволяет выбрать SSH-порт, включает fail2ban или отдельно добавляет правила UFW | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | @@ -88,12 +88,17 @@ timedatectl Файл: `ochenstarik-server-user-3.sh`. -Скрипт интерактивно запрашивает имя нового администратора, SSH-ключ и пароль. Затем он: +При каждом запуске скрипт предлагает два режима: + +1. новая установка или повторная настройка администратора и SSH; +2. только добавление открытых портов в UFW без изменения пользователя и SSH. + +В режиме новой установки скрипт интерактивно запрашивает имя администратора, SSH-порт, SSH-ключ и пароль. Затем он: - создаёт или обновляет пользователя; - добавляет пользователя в группу `sudo`; - запрещает вход `root` по SSH; -- переносит SSH на порт `20202`; +- переносит SSH на выбранный порт и добавляет для него правило UFW; - настраивает fail2ban; - по выбору управляет правилами UFW. @@ -111,7 +116,7 @@ sudo ./ochenstarik-server-user-3.sh Не закрывая текущую сессию, откройте второй терминал и проверьте вход: ```bash -ssh -p 20202 ИМЯ_ПОЛЬЗОВАТЕЛЯ@IP_СЕРВЕРА +ssh -p ВЫБРАННЫЙ_ПОРТ ИМЯ_ПОЛЬЗОВАТЕЛЯ@IP_СЕРВЕРА ``` Только после успешной проверки можно удалить временное правило порта 22: @@ -120,6 +125,14 @@ ssh -p 20202 ИМЯ_ПОЛЬЗОВАТЕЛЯ@IP_СЕРВЕРА sudo ufw delete allow 22/tcp ``` +Чтобы позднее открыть дополнительные порты, повторно запустите скрипт, выберите пункт `2` и введите правила через пробел или запятую: + +```text +80 443/tcp 53/udp 40000 +``` + +Для записей без протокола используется TCP. Допустимы только порты от `1` до `65535`. Если UFW неактивен, правила будут сохранены, но скрипт не включит брандмауэр автоматически во избежание потери SSH-доступа. + ## 4. Telegram-уведомления о входах по SSH Файл: `ochenstarik-server-tg-4.sh`. diff --git a/ochenstarik-server-user-3.sh b/ochenstarik-server-user-3.sh index 896a702..14df4ad 100644 --- a/ochenstarik-server-user-3.sh +++ b/ochenstarik-server-user-3.sh @@ -57,8 +57,102 @@ require_command() { command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" } +is_valid_port() { + local port="$1" + [[ "$port" =~ ^[0-9]{1,5}$ ]] || return 1 + (( 10#$port >= 1 && 10#$port <= 65535 )) +} + +select_action() { + local choice + + printf '\nSelect an action:\n' + printf ' 1 - new installation or reconfigure the administrative user and SSH\n' + printf ' 2 - add open ports to UFW without changing SSH or users\n' + while :; do + read -rp 'Select [1]: ' choice + choice="${choice:-1}" + case "$choice" in + 1) + ACTION="install" + return 0 + ;; + 2) + ACTION="add-ports" + return 0 + ;; + *) + warn "Select 1 or 2" + ;; + esac + done +} + +add_ufw_ports_interactive() { + local input normalized token port protocol rule + local -a tokens rules + local -A seen + + require_command ufw + printf '\nEnter one or more ports separated by spaces or commas.\n' + printf 'TCP is used by default; specify UDP explicitly when needed.\n' + printf 'Example: 80 443/tcp 53/udp 40000\n' + + while :; do + read -rp 'Ports to open: ' input + normalized="${input//,/ }" + read -r -a tokens <<< "$normalized" + rules=() + seen=() + + if (( ${#tokens[@]} == 0 )); then + warn "Enter at least one port" + continue + fi + + for token in "${tokens[@]}"; do + if [[ ! "$token" =~ ^([0-9]+)(/(tcp|udp))?$ ]]; then + warn "Invalid rule: $token; use PORT, PORT/tcp or PORT/udp" + rules=() + break + fi + + port="${BASH_REMATCH[1]}" + protocol="${BASH_REMATCH[3]:-tcp}" + if ! is_valid_port "$port"; then + warn "Port must be between 1 and 65535: $port" + rules=() + break + fi + port="$((10#$port))" + + rule="${port}/${protocol}" + if [[ -z "${seen[$rule]:-}" ]]; then + rules+=("$rule") + seen["$rule"]=1 + fi + done + + (( ${#rules[@]} > 0 )) && break + done + + for rule in "${rules[@]}"; do + log "Allowing ${rule} in UFW" + ufw allow "$rule" + done + + if ! LANG=C ufw status | grep -q '^Status: active'; then + warn "UFW is inactive. Rules were added but the firewall was not enabled automatically." + warn "Review SSH access first, then enable it with: ufw enable" + fi + + printf '\nCurrent UFW status:\n' + ufw status verbose +} + collect_user_configuration() { - local new_username ssh_public_key pass pass2 candidate_uid + local new_username ssh_port default_ssh_port saved_ssh_port + local ssh_public_key pass pass2 candidate_uid local key_method source_username source_home source_key_file candidate_key key_test log "Collecting configuration for a new administrative user" @@ -79,6 +173,24 @@ collect_user_configuration() { break done + default_ssh_port="20202" + if [[ -f "$ENV_FILE" ]]; then + saved_ssh_port="$(read_env_value SSH_PORT)" + if is_valid_port "$saved_ssh_port"; then + default_ssh_port="$saved_ssh_port" + fi + fi + + while :; do + read -rp "SSH port [${default_ssh_port}]: " ssh_port + ssh_port="${ssh_port:-$default_ssh_port}" + if is_valid_port "$ssh_port"; then + ssh_port="$((10#$ssh_port))" + break + fi + warn "SSH port must be a number between 1 and 65535" + done + while :; do printf '\nSSH key setup:\n' printf ' 1 - copy a working key from an existing user (recommended)\n' @@ -151,7 +263,7 @@ collect_user_configuration() { printf '%s' "$pass" | openssl passwd -6 -stdin > "$HASH_FILE" cat > "$ENV_FILE" </dev/null 2>&1; then existing_uid="$(id -u "$NEW_USERNAME")" (( existing_uid >= 1000 && existing_uid != 65534 )) || die "Refusing to modify a system account: $NEW_USERNAME" fi -[[ "$SSH_PORT" =~ ^[0-9]+$ ]] || die "SSH_PORT must be numeric" -(( 10#$SSH_PORT >= 1 && 10#$SSH_PORT <= 65535 )) || die "SSH_PORT must be between 1 and 65535" +is_valid_port "$SSH_PORT" || die "SSH_PORT must be between 1 and 65535" validate_boolean PASSWORD_AUTH "$PASSWORD_AUTH" validate_boolean PASSWORDLESS_SUDO "$PASSWORDLESS_SUDO" validate_boolean MANAGE_UFW "$MANAGE_UFW"