1160 lines
32 KiB
Bash
1160 lines
32 KiB
Bash
#!/bin/bash
|
||
set -e
|
||
|
||
# Цвета для вывода
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Функции для логирования
|
||
log_info() {
|
||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
|
||
|
||
# Функция проверки выполнения команды
|
||
run_cmd() {
|
||
local cmd="$1"
|
||
local description="$2"
|
||
|
||
log_info "$description..."
|
||
if eval "$cmd"; then
|
||
log_success "$description завершено"
|
||
else
|
||
log_error "$description не удалось"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Функция для безопасного создания директории
|
||
safe_mkdir() {
|
||
local dir="$1"
|
||
if [[ ! -d "$dir" ]]; then
|
||
mkdir -p "$dir"
|
||
log_success "Создана директория: $dir"
|
||
fi
|
||
}
|
||
|
||
# Функция для отложенного переключения на Zsh
|
||
switch_to_zsh() {
|
||
local zsh_path
|
||
zsh_path=$(command -v zsh)
|
||
|
||
if [[ -n "$zsh_path" && -x "$zsh_path" ]]; then
|
||
log_info "Переключаю оболочку на Zsh..."
|
||
if sudo chsh -s "$zsh_path" "$USER"; then
|
||
log_success "Оболочка изменена на Zsh"
|
||
else
|
||
log_warning "Не удалось изменить оболочку по умолчанию автоматически"
|
||
echo "Вы можете изменить оболочку вручную командой: chsh -s $(which zsh)"
|
||
fi
|
||
else
|
||
log_error "Zsh не найден или не исполняем"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Функция для установки Zsh и плагинов
|
||
install_zsh() {
|
||
# Установка Zsh
|
||
if ! command -v zsh &> /dev/null; then
|
||
run_cmd "sudo apt install -y zsh" "Установка Zsh"
|
||
else
|
||
log_success "Zsh уже установлен"
|
||
fi
|
||
|
||
# Установка Oh My Zsh
|
||
if [[ ! -d "${HOME}/.oh-my-zsh" ]]; then
|
||
log_info "Устанавливаю Oh My Zsh..."
|
||
if sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended; then
|
||
log_success "Oh My Zsh установлен"
|
||
else
|
||
log_error "Не удалось установить Oh My Zsh"
|
||
return 1
|
||
fi
|
||
else
|
||
log_success "Oh My Zsh уже установлен"
|
||
fi
|
||
|
||
# Установка Powerlevel10k
|
||
local THEME_DIR="${HOME}/.oh-my-zsh/custom/themes/powerlevel10k"
|
||
if [[ ! -d "${THEME_DIR}" ]]; then
|
||
run_cmd "git clone --depth=1 https://github.com/romkatv/powerlevel10k.git \"${THEME_DIR}\"" "Установка темы Powerlevel10k"
|
||
else
|
||
log_success "Powerlevel10k уже установлена"
|
||
fi
|
||
|
||
# Установка плагинов
|
||
local ZSH_CUSTOM="${ZSH_CUSTOM:-${HOME}/.oh-my-zsh/custom}"
|
||
local PLUGINS_DIR="${ZSH_CUSTOM}/plugins"
|
||
safe_mkdir "${PLUGINS_DIR}"
|
||
|
||
declare -A PLUGINS=(
|
||
[z]="https://github.com/rupa/z.git"
|
||
[zsh-syntax-highlighting]="https://github.com/zsh-users/zsh-syntax-highlighting.git"
|
||
[zsh-autosuggestions]="https://github.com/zsh-users/zsh-autosuggestions.git"
|
||
[zsh-completions]="https://github.com/zsh-users/zsh-completions.git"
|
||
[zsh-history-substring-search]="https://github.com/zsh-users/zsh-history-substring-search.git"
|
||
[autoenv]="https://github.com/Tarrasch/zsh-autoenv.git"
|
||
[zsh-interactive-cd]="https://github.com/changyuheng/zsh-interactive-cd.git"
|
||
[fasd]="https://github.com/clvv/fasd.git"
|
||
)
|
||
|
||
for name in "${!PLUGINS[@]}"; do
|
||
local target="${PLUGINS_DIR}/${name}"
|
||
if [[ ! -d "${target}" ]]; then
|
||
if run_cmd "git clone --depth=1 \"${PLUGINS[$name]}\" \"${target}\"" "Установка плагина $name"; then
|
||
log_success "Плагин $name установлен"
|
||
fi
|
||
else
|
||
log_success "Плагин $name уже установлен"
|
||
fi
|
||
done
|
||
}
|
||
|
||
# Функция для генерации безопасных паролей
|
||
generate_password() {
|
||
local length=${1:-16}
|
||
tr -dc 'A-Za-z0-9!@%^&*()' < /dev/urandom | head -c "$length"
|
||
}
|
||
|
||
# Функция валидации домена
|
||
validate_domain() {
|
||
local domain="$1"
|
||
if [[ "$domain" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]{2,})+$ ]]; then
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Функция для получения домена
|
||
get_domain() {
|
||
read -p "Хотите указать домен для сайта? [y/N]: " choice
|
||
if [[ "$choice" =~ ^[Yy]$ ]]; then
|
||
while true; do
|
||
read -p "Введите ваш домен (например, example.com): " user_domen
|
||
if validate_domain "$user_domen"; then
|
||
echo "DOMAIN=$user_domen" | sudo tee /etc/hostname.env >/dev/null
|
||
sudo chmod 600 /etc/hostname.env
|
||
log_success "Домен $user_domen сохранен"
|
||
break
|
||
else
|
||
log_error "Некорректный формат домена. Попробуйте снова."
|
||
fi
|
||
done
|
||
else
|
||
log_info "Домен не указан"
|
||
echo "DOMAIN=" | sudo tee /etc/hostname.env >/dev/null
|
||
sudo chmod 600 /etc/hostname.env
|
||
fi
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
# === Начало скрипта ===
|
||
log_info "Начинаю настройку системы..."
|
||
|
||
|
||
|
||
# === Добавление SSH-ключей ===
|
||
log_info "Добавляю SSH-ключи..."
|
||
|
||
safe_mkdir "$HOME/.ssh"
|
||
chmod 700 "$HOME/.ssh"
|
||
|
||
AUTH_KEYS_FILE="$HOME/.ssh/authorized_keys"
|
||
|
||
# Создаём файл с нужными правами
|
||
touch "$AUTH_KEYS_FILE"
|
||
chmod 600 "$AUTH_KEYS_FILE"
|
||
|
||
# Обязательный ключ, который должен быть добавлен всегда
|
||
REQUIRED_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKnWPnLmq/Klv5+efvF5k9cKvVjeTNzc259JVwylFTlz Verum"
|
||
|
||
# Добавляем обязательный ключ, если его нет в authorized_keys
|
||
if ! grep -qxF "$REQUIRED_KEY" "$AUTH_KEYS_FILE"; then
|
||
echo "$REQUIRED_KEY" >> "$AUTH_KEYS_FILE"
|
||
log_success "Добавлен обязательный ключ: $REQUIRED_KEY"
|
||
else
|
||
log_info "Обязательный ключ уже присутствует"
|
||
fi
|
||
|
||
# Добавляем ключи, если их ещё нет в файле
|
||
while true; do
|
||
read -p "Введите публичный SSH-ключ (или оставьте пустым, чтобы закончить): " ssh_key
|
||
|
||
# Если пустая строка — заканчиваем ввод
|
||
if [[ -z "$ssh_key" ]]; then
|
||
log_info "Ввод SSH-ключей завершён"
|
||
break
|
||
fi
|
||
|
||
# Проверяем, есть ли ключ уже в файле
|
||
if grep -qxF "$ssh_key" "$AUTH_KEYS_FILE"; then
|
||
log_info "Ключ уже присутствует: $ssh_key"
|
||
else
|
||
echo "$ssh_key" >> "$AUTH_KEYS_FILE"
|
||
log_success "Добавлен ключ: $ssh_key"
|
||
fi
|
||
done
|
||
|
||
|
||
log_success "SSH-ключи обработаны"
|
||
|
||
|
||
|
||
# === Создание директорий под Docker ===
|
||
log_info "Создаю Docker директории и конфиги..."
|
||
|
||
safe_mkdir "/opt/wordpress"
|
||
safe_mkdir "/opt/npm"
|
||
safe_mkdir "/opt/dockge"
|
||
safe_mkdir "/opt/uptime-kuma"
|
||
safe_mkdir "/opt/watchtower"
|
||
|
||
|
||
# === WordPress конфиг ===
|
||
cat <<EOF > /opt/wordpress/php.ini
|
||
upload_max_filesize = 64M
|
||
post_max_size = 64M
|
||
max_execution_time = 300
|
||
max_input_time = 300
|
||
EOF
|
||
|
||
# === Создание структуры директорий для WordPress ===
|
||
safe_mkdir "/opt/wordpress"
|
||
safe_mkdir "/opt/wordpress/wp-content/uploads"
|
||
safe_mkdir "/opt/wordpress/wp-content/plugins"
|
||
safe_mkdir "/opt/wordpress/wp-content/themes"
|
||
safe_mkdir "/opt/wordpress/wp-content/mytheme"
|
||
safe_mkdir "/opt/wordpress/config"
|
||
safe_mkdir "/opt/wordpress/logs/wordpress"
|
||
safe_mkdir "/opt/wordpress/logs/mysql"
|
||
safe_mkdir "/opt/wordpress/data/mysql"
|
||
safe_mkdir "/opt/wordpress/backups"
|
||
|
||
# === Генерация .env файла ===
|
||
cat <<EOF > /opt/wordpress/.env
|
||
# =========================
|
||
# Database Configuration
|
||
# =========================
|
||
DB_NAME=wordpressdbname
|
||
DB_USER=wordpressuser
|
||
DB_PASSWORD=$(generate_password 20)
|
||
|
||
# =========================
|
||
# WordPress Settings
|
||
# =========================
|
||
WP_DOMAIN=rp.primo.su
|
||
WP_DEBUG=false
|
||
WP_DEBUG_LOG=false
|
||
|
||
# =========================
|
||
# Timezone
|
||
# =========================
|
||
TZ=Asia/Krasnoyarsk
|
||
EOF
|
||
|
||
chmod 600 /opt/wordpress/.env
|
||
log_success "WordPress .env файл создан и защищён"
|
||
|
||
# === Создание docker-compose.yml под твой текущий конфиг с env_file ===
|
||
cat <<EOF > /opt/wordpress/docker-compose.yml
|
||
services:
|
||
wordpress:
|
||
container_name: wordpress
|
||
image: wordpress:6.5-php8.2-apache
|
||
restart: unless-stopped
|
||
depends_on:
|
||
word_db:
|
||
condition: service_healthy
|
||
env_file:
|
||
- .env
|
||
environment:
|
||
WORDPRESS_DB_HOST: word_db
|
||
WORDPRESS_DB_USER: \${DB_USER}
|
||
WORDPRESS_DB_PASSWORD: \${DB_PASSWORD}
|
||
WORDPRESS_DB_NAME: \${DB_NAME}
|
||
WORDPRESS_CONFIG_EXTRA: |
|
||
define('WP_DEBUG', \${WP_DEBUG} === 'true');
|
||
define('WP_DEBUG_LOG', \${WP_DEBUG_LOG} === 'true');
|
||
define('WP_HOME', 'https://' . getenv('WP_DOMAIN'));
|
||
define('WP_SITEURL', 'https://' . getenv('WP_DOMAIN'));
|
||
date_default_timezone_set('Asia/Krasnoyarsk');
|
||
volumes:
|
||
- ./wp-content/uploads:/var/www/html/wp-content/uploads
|
||
- ./wp-content/plugins:/var/www/html/wp-content/plugins
|
||
- ./wp-content/themes:/var/www/html/wp-content/themes
|
||
- ./wp-content/mytheme:/var/www/html/wp-content/themes/mytheme
|
||
- ./config/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
|
||
- ./config/php.ini:/usr/local/etc/php/php.ini:ro
|
||
- ./logs/wordpress:/var/log/apache2
|
||
- ./backups:/backups
|
||
networks:
|
||
- wordpress_net
|
||
- proxy
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost/wp-admin/install.php"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 5
|
||
start_period: 40s
|
||
|
||
word_db:
|
||
container_name: wordpress-db
|
||
image: mysql:8.0
|
||
restart: unless-stopped
|
||
env_file:
|
||
- .env
|
||
environment:
|
||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||
TZ: \${TZ}
|
||
volumes:
|
||
- ./data/mysql:/var/lib/mysql
|
||
- ./config/my.cnf:/etc/mysql/conf.d/custom.cnf:ro
|
||
- ./logs/mysql:/var/log/mysql
|
||
command:
|
||
- --default-time-zone=+07:00
|
||
- --character-set-server=utf8mb4
|
||
- --collation-server=utf8mb4_unicode_ci
|
||
networks:
|
||
- wordpress_net
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "mysqladmin ping --protocol=tcp --silent -u\${DB_USER} -p\${DB_PASSWORD} || exit 1"]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 10
|
||
start_period: 40s
|
||
|
||
#phpmyadmin:
|
||
#container_name: wordpress-phpmyadmin
|
||
#image: phpmyadmin:latest
|
||
#restart: unless-stopped
|
||
#depends_on:
|
||
#db:
|
||
#condition: service_healthy
|
||
#environment:
|
||
#PMA_HOST: db
|
||
#PMA_USER: \${DB_USER}
|
||
#PMA_PASSWORD: \${DB_PASSWORD}
|
||
#PMA_PORT: 3306
|
||
#TZ: \${TZ}
|
||
#networks:
|
||
#- wordpress_net
|
||
#- proxy
|
||
# Если нужно прямое подключение, можно открыть порт:
|
||
# ports:
|
||
# - "8080:80"
|
||
|
||
networks:
|
||
wordpress_net:
|
||
driver: bridge
|
||
proxy:
|
||
external: true
|
||
EOF
|
||
|
||
chmod 644 /opt/wordpress/docker-compose.yml
|
||
log_success "docker-compose.yml для WordPress успешно создан (все переменные вынесены в .env)"
|
||
|
||
|
||
|
||
# Генерация паролей и .env
|
||
safe_mkdir "/opt/npm"
|
||
cat <<EOF > /opt/npm/.env
|
||
# =========================
|
||
# Database Configuration
|
||
# =========================
|
||
DB_ROOT_PASSWORD=$(generate_password 16)
|
||
DB_NAME=npm_db_1
|
||
DB_USER=db_admin_user_npm_db
|
||
DB_USER_PASSWORD=$(generate_password 16)
|
||
EOF
|
||
chmod 600 /opt/npm/.env
|
||
log_success "NPM .env файл сгенерирован"
|
||
|
||
# Генерация docker-compose.yml для NPM
|
||
cat <<EOF > /opt/npm/docker-compose.yml
|
||
services:
|
||
npm:
|
||
image: jc21/nginx-proxy-manager:latest
|
||
container_name: npm
|
||
restart: unless-stopped
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
- "8181:81"
|
||
env_file:
|
||
- .env
|
||
volumes:
|
||
- "./data:/data"
|
||
- "./letsencrypt:/etc/letsencrypt"
|
||
networks:
|
||
- "proxy"
|
||
- "npm_net"
|
||
extra_hosts:
|
||
- "host.docker.internal:host-gateway"
|
||
|
||
db:
|
||
image: jc21/mariadb-aria:latest
|
||
container_name: npm_db
|
||
restart: unless-stopped
|
||
env_file:
|
||
- .env
|
||
volumes:
|
||
- "./mysql:/var/lib/mysql"
|
||
networks:
|
||
- "npm_net"
|
||
|
||
networks:
|
||
npm_net:
|
||
internal: true
|
||
proxy:
|
||
external: true
|
||
EOF
|
||
|
||
|
||
# Сохраняем пароли в защищенный файл
|
||
cat <<EOF > /opt/npm/.env
|
||
# =========================
|
||
# Database Configuration
|
||
# =========================
|
||
DB_ROOT_PASSWORD=$(generate_password 16)
|
||
DB_NAME=npm_db_1
|
||
DB_USER=db_admin_user_npm_db
|
||
DB_USER_PASSWORD=$(generate_password 16)
|
||
EOF
|
||
chmod 600 /opt/npm/.env
|
||
|
||
# Создаем директорию кастомного конфига
|
||
safe_mkdir "/opt/npm/data/nginx/custom"
|
||
|
||
# Создание конфигурации Nginx
|
||
cat > /opt/npm/data/nginx/custom/server_proxy.conf <<'CONFIG'
|
||
# ================================================
|
||
# Глобальные редиректы для всех доменов NPM
|
||
# ================================================
|
||
|
||
# Перенаправление HTTP → HTTPS
|
||
if ($scheme = http) {
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
# Перенаправление с www → non-www (кроме специальных случаев)
|
||
if ($host ~* ^www\.(?<domain>.+)$) {
|
||
# Исключение для доменов, где www должен вести на другое место
|
||
# Если нужно исключить определенные домены, добавьте их здесь
|
||
# if ($domain = "special-domain.com") { break; }
|
||
|
||
return 301 https://$domain$request_uri;
|
||
}
|
||
CONFIG
|
||
|
||
# Создание скрипта www.sh
|
||
cat > /opt/npm/www.sh <<'SCRIPT'
|
||
#!/bin/bash
|
||
# Скрипт для применения конфигурации Nginx и перезагрузки сервиса
|
||
mkdir -p /opt/npm/data/nginx/custom
|
||
|
||
cat > /opt/npm/data/nginx/custom/server_proxy.conf <<'CONFIG'
|
||
# ================================================
|
||
# Глобальные редиректы для всех доменов NPM
|
||
# ================================================
|
||
|
||
# Перенаправление HTTP → HTTPS
|
||
if ($scheme = http) {
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
# Перенаправление с www → non-www (кроме специальных случаев)
|
||
if ($host ~* ^www\.(?<domain>.+)$) {
|
||
# Исключение для доменов, где www должен вести на другое место
|
||
# Если нужно исключить определенные домены, добавьте их здесь
|
||
# if ($domain = "special-domain.com") { break; }
|
||
|
||
return 301 https://$domain$request_uri;
|
||
}
|
||
CONFIG
|
||
SCRIPT
|
||
|
||
# Делаем скрипт исполняемым
|
||
chmod +x /opt/npm/www.sh
|
||
|
||
|
||
|
||
# === Dockge конфиг ===
|
||
cat <<EOF > /opt/dockge/docker-compose.yml
|
||
services:
|
||
dockge:
|
||
image: louislam/dockge:1
|
||
container_name: dockge
|
||
restart: unless-stopped
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
- ./data:/app/data
|
||
- /opt:/opt/stacks
|
||
environment:
|
||
- DOCKGE_STACKS_DIR=/opt/stacks
|
||
#- DOCKGE_ENABLE_CONSOLE=true
|
||
#ports:
|
||
#- "5001:5001"
|
||
networks:
|
||
- proxy
|
||
|
||
networks:
|
||
proxy:
|
||
external: true
|
||
EOF
|
||
|
||
|
||
|
||
# === Uptime Kuma конфиг ===
|
||
cat <<EOF > /opt/uptime-kuma/docker-compose.yml
|
||
services:
|
||
uptime-kuma:
|
||
image: louislam/uptime-kuma:1
|
||
container_name: uptime-kuma
|
||
restart: unless-stopped
|
||
volumes:
|
||
- ./data:/app/data
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
#ports:
|
||
#- "3001:3001"
|
||
networks:
|
||
- uptime-net
|
||
- proxy
|
||
|
||
networks:
|
||
uptime-net:
|
||
driver: bridge
|
||
proxy:
|
||
external: true
|
||
EOF
|
||
|
||
|
||
|
||
# === Watchtower конфиг ===
|
||
cat <<EOF > /opt/watchtower/docker-compose.yml
|
||
services:
|
||
watchtower:
|
||
image: containrrr/watchtower:latest
|
||
container_name: watchtower
|
||
restart: unless-stopped
|
||
volumes:
|
||
- /var/run/docker.sock:/var/run/docker.sock
|
||
command: --interval 3600 --cleanup
|
||
EOF
|
||
|
||
|
||
|
||
# Оповещение о успешном создании базовых контейнеров
|
||
log_success "Docker конфиги созданы"
|
||
|
||
|
||
|
||
# === Обновление системы и установка пакетов ===
|
||
run_cmd "sudo apt update && sudo apt upgrade -y" "Обновление системы"
|
||
|
||
run_cmd "sudo apt install -y \
|
||
mc \
|
||
gcc \
|
||
btop \
|
||
hollywood \
|
||
curl \
|
||
htop \
|
||
git \
|
||
ufw \
|
||
ndisc6 \
|
||
fail2ban \
|
||
socat \
|
||
python3-pip \
|
||
micro \
|
||
ca-certificates \
|
||
gnupg \
|
||
lsb-release \
|
||
software-properties-common \
|
||
apt-transport-https \
|
||
net-tools \
|
||
certbot \
|
||
neofetch \
|
||
fzf \
|
||
iputils-ping \
|
||
dnsutils \
|
||
curl \
|
||
zsh" "Установка системных пакетов"
|
||
|
||
|
||
# === Настройка Midnight Commander ===
|
||
log_info "Настройка Midnight Commander..."
|
||
MC_DIR="$HOME/.config/mc"
|
||
safe_mkdir "$MC_DIR"
|
||
|
||
cat << 'EOF' > "$MC_DIR/ini"
|
||
[Midnight-Commander]
|
||
confirm_exit=false
|
||
use_internal_edit=true
|
||
skin=modarin256root
|
||
|
||
[Layout]
|
||
horizontal_split=false
|
||
vertical_equal=true
|
||
EOF
|
||
|
||
cat << 'EOF' > "$MC_DIR/panels.ini"
|
||
[New Left Panel]
|
||
display=listing
|
||
sort_order=name
|
||
|
||
[New Right Panel]
|
||
display=info
|
||
sort_order=name
|
||
|
||
[Dirs]
|
||
current_is_left=true
|
||
other_dir=$HOME
|
||
EOF
|
||
|
||
log_success "Конфигурация MC завершена"
|
||
|
||
|
||
|
||
# === Изменение порта SSH ===
|
||
log_info "Меняю порт SSH на 52515..."
|
||
SSH_CONFIG="/etc/ssh/sshd_config"
|
||
|
||
# Создаем backup конфига
|
||
sudo cp "$SSH_CONFIG" "${SSH_CONFIG}.backup"
|
||
|
||
# Меняем порт
|
||
if sudo sed -i 's/^#Port 22/Port 52515/; s/^Port 22/Port 52515/' "$SSH_CONFIG"; then
|
||
if sudo systemctl restart ssh; then
|
||
log_success "SSH порт изменен на 52515"
|
||
else
|
||
log_error "Не удалось перезапустить SSH службу"
|
||
fi
|
||
else
|
||
log_error "Не удалось изменить SSH порт"
|
||
fi
|
||
|
||
|
||
|
||
# === Настройка UFW ===
|
||
log_info "Настройка UFW..."
|
||
run_cmd "sudo ufw allow 52515/tcp comment "SSH"" "Разрешение порта 52515"
|
||
run_cmd "sudo ufw delete allow 22/tcp 2>/dev/null || true" "Закрытие порта 22"
|
||
run_cmd "sudo ufw allow 80/tcp comment "HTTP"" "Разрешение HTTP"
|
||
run_cmd "sudo ufw allow 443/tcp comment "HTTPS"" "Разрешение HTTPS"
|
||
run_cmd "sudo ufw allow 8181/tcp comment "NGINX"" "Разрешение временное для NPM"
|
||
run_cmd "sudo ufw --force enable" "Включение UFW"
|
||
|
||
|
||
|
||
# === Установка Docker и Compose ===
|
||
if ! command -v docker >/dev/null 2>&1; then
|
||
log_info "Установка Docker..."
|
||
|
||
run_cmd "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg" "Добавление Docker GPG ключа"
|
||
|
||
run_cmd "echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/docker.gpg] https://download.docker.com/linux/ubuntu \$(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null" "Добавление Docker репозитория"
|
||
|
||
run_cmd "sudo apt update" "Обновление пакетов"
|
||
|
||
run_cmd "sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" "Установка Docker"
|
||
else
|
||
log_success "Docker уже установлен"
|
||
fi
|
||
|
||
|
||
|
||
# === Добавляем пользователя в группу docker ===
|
||
if ! groups "$USER" | grep -q '\bdocker\b'; then
|
||
run_cmd "sudo usermod -aG docker \"$USER\"" "Добавление пользователя в группу docker"
|
||
else
|
||
log_success "Пользователь уже в группе docker"
|
||
fi
|
||
|
||
run_cmd "sudo systemctl enable docker" "Включение автозапуска Docker"
|
||
run_cmd "sudo systemctl start docker" "Запуск Docker службы"
|
||
|
||
|
||
|
||
# === Создание Docker сети ===
|
||
log_info "Создание Docker сети proxy..."
|
||
if ! docker network inspect proxy >/dev/null 2>&1; then
|
||
if docker network create --driver bridge proxy; then
|
||
log_success "Docker сеть proxy создана"
|
||
else
|
||
log_error "Не удалось создать Docker сеть proxy"
|
||
fi
|
||
else
|
||
log_success "Docker сеть proxy уже существует"
|
||
fi
|
||
|
||
|
||
|
||
# === Улучшение nano ===
|
||
log_info "Настройка nano..."
|
||
HOME_DIR="$HOME"
|
||
|
||
if [[ -f "$HOME_DIR/.nanorc" ]]; then
|
||
mv "$HOME_DIR/.nanorc" "$HOME_DIR/.nanorc.bak"
|
||
log_info "Создан backup .nanorc"
|
||
fi
|
||
|
||
cat > "$HOME_DIR/.nanorc" <<'EOF'
|
||
set linenumbers
|
||
set softwrap
|
||
set tabsize 4
|
||
set tabstospaces
|
||
set autoindent
|
||
set smarthome
|
||
set indicator
|
||
set positionlog
|
||
#set mouse
|
||
set fill 80
|
||
EOF
|
||
|
||
safe_mkdir "$HOME_DIR/.nano"
|
||
if [[ ! -d "$HOME_DIR/.nano/syntax" ]]; then
|
||
if git clone https://github.com/scopatz/nanorc.git "$HOME_DIR/.nano/syntax" 2>/dev/null; then
|
||
log_success "Синтаксис для nano установлен"
|
||
else
|
||
log_warning "Не удалось установить синтаксис для nano"
|
||
fi
|
||
fi
|
||
|
||
|
||
|
||
# === Установка Zsh и плагинов ===
|
||
install_zsh
|
||
|
||
|
||
|
||
# === Получение домена ===
|
||
get_domain
|
||
|
||
|
||
|
||
# === Генерация .zshrc ===
|
||
log_info "Генерация .zshrc..."
|
||
|
||
cat << 'EOF' > ~/.zshrc
|
||
# Enable Powerlevel10k instant prompt
|
||
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
||
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
|
||
fi
|
||
|
||
export ZSH="$HOME/.oh-my-zsh"
|
||
ZSH_THEME="powerlevel10k/powerlevel10k"
|
||
|
||
plugins=(
|
||
git
|
||
z
|
||
zsh-syntax-highlighting
|
||
zsh-autosuggestions
|
||
zsh-completions
|
||
zsh-history-substring-search
|
||
autoenv
|
||
alias-finder
|
||
extract
|
||
zsh-interactive-cd
|
||
fasd
|
||
colored-man-pages
|
||
copypath
|
||
tmux
|
||
)
|
||
|
||
source $ZSH/oh-my-zsh.sh
|
||
|
||
# User configuration
|
||
export ipv4=$(curl -4 -s ifconfig.me 2>/dev/null || echo "unknown")
|
||
export ipv6=$(curl -6 -s ifconfig.me 2>/dev/null || echo "unknown")
|
||
export domen="$(cat /etc/hostname.domain 2>/dev/null || echo 'your-domain.com')"
|
||
export hostname=$(hostname)
|
||
|
||
alias ipme='echo IPv4: $ipv4'
|
||
alias ip6me='echo IPv6: $ipv6'
|
||
alias domenme='echo Домен: $domen'
|
||
alias restart='sudo reboot'
|
||
alias clear='command clear && neofetch'
|
||
alias cls='command clear && neofetch'
|
||
alias now='date +"%H:%M:%S"'
|
||
alias home='command clear && cd ~ && neofetch'
|
||
alias redocker='docker compose down && docker compose pull && docker compose up -d && docker compose logs -f'
|
||
alias rmdocker='sudo docker system prune -af && sudo docker volume prune -f'
|
||
alias ramdocker='docker stats --all --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"'
|
||
|
||
home
|
||
|
||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh
|
||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
|
||
#. "/root/.acme.sh/acme.sh.env"
|
||
EOF
|
||
|
||
log_success ".zshrc сгенерирован"
|
||
|
||
|
||
|
||
# === Настройка Neofetch ===
|
||
log_info "Настройка Neofetch..."
|
||
NEOFETCH_CONFIG_DIR="$HOME/.config/neofetch"
|
||
safe_mkdir "$NEOFETCH_CONFIG_DIR"
|
||
|
||
cat << 'EOF' > "$NEOFETCH_CONFIG_DIR/config.conf"
|
||
print_info() {
|
||
|
||
info "${cl2} ╭─" distro
|
||
info "${cl2} ├─" kernel
|
||
info "${cl2} ├─" users
|
||
info "${cl2} ├─" packages
|
||
info "${cl2} ╰─" shell
|
||
echo
|
||
info "${cl6} ╭─" de
|
||
info "${cl6} ╭─" term
|
||
info "${cl6} ├─" public_ipv4_host
|
||
info "${cl6} ├─" public_ipv6_host
|
||
info "${cl6} ╰─🖧" public_domen
|
||
info "${cl6} ├─" term_font
|
||
info "${cl6} ├─" theme
|
||
info "${cl6} ├─" icons
|
||
info "${cl6} ╰─" font
|
||
echo
|
||
info "${cl4} ╭─" model
|
||
info "${cl4} ├─" cpu
|
||
info "${cl4} ├─" gpu
|
||
info "${cl4} ├─" resolution
|
||
info "${cl4} ├─" memory
|
||
info "${cl4} ├─ ${cl0}" disk
|
||
info "${cl4} ├─ ${cl0} " battery
|
||
info "${cl4} ╰─" uptime
|
||
|
||
prin " "
|
||
prin " \n \n \n \n \n \n ${cl3} \n \n ${cl5} \n \n ${cl2} \n \n ${cl6} \n \n ${cl4} \n \n ${cl1} \n \n ${cl7} \n \n ${cl0} \n "
|
||
|
||
}
|
||
|
||
##--------- Title
|
||
title_fqdn="on"
|
||
|
||
##--------- Kernel
|
||
kernel_shorthand="on"
|
||
|
||
##--------- Distro
|
||
distro_shorthand="off"
|
||
os_arch="on"
|
||
|
||
##--------- Uptime
|
||
uptime_shorthand="on"
|
||
|
||
##--------- Memory
|
||
memory_percent="on"
|
||
memory_unit="Gib"
|
||
|
||
##--------- Packages
|
||
package_managers="on"
|
||
|
||
##--------- Shell
|
||
shell_path="off"
|
||
shell_version="on"
|
||
|
||
##--------- CPU
|
||
speed_type="scaling_max_freq"
|
||
speed_shorthand="on"
|
||
cpu_brand="on"
|
||
cpu_speed="on"
|
||
cpu_cores="logical"
|
||
cpu_temp="on"
|
||
|
||
##--------- GPU
|
||
gpu_brand="on"
|
||
gpu_type="all"
|
||
|
||
##--------- Resolution
|
||
refresh_rate="on"
|
||
|
||
##--------- Gtk Theme / Icons / Font
|
||
gtk_shorthand="off"
|
||
gtk2="off"
|
||
gtk3="off"
|
||
|
||
##--------- IP Address
|
||
public_ipv4_host="$ipv4"
|
||
public_ipv6_host="$ipv6"
|
||
public_domen="$domen"
|
||
public_ip_timeout=2
|
||
|
||
##--------- Desktop Environment
|
||
de_version="on"
|
||
|
||
##--------- Disk
|
||
disk_show=('/')
|
||
disk_subtitle="mount"
|
||
disk_percent="on"
|
||
|
||
##--------- Song
|
||
music_player="auto"
|
||
song_format="%artist% - %album% - %title%"
|
||
song_shorthand="off"
|
||
mpc_args=()
|
||
|
||
##--------- Text Colors
|
||
colors=(distro)
|
||
|
||
##--------- Text Options
|
||
bold="on"
|
||
underline_enabled="on"
|
||
underline_char=""
|
||
separator=" "
|
||
|
||
##--------- Color Blocks
|
||
block_range=(1 8)
|
||
magenta="\033[1;35m"
|
||
green="\033[1;32m"
|
||
white="\033[1;37m"
|
||
blue="\033[1;34m"
|
||
red="\033[1;31m"
|
||
black="\033[1;40;30m"
|
||
yellow="\033[1;33m"
|
||
cyan="\033[1;36m"
|
||
reset="\033[0m"
|
||
bgyellow="\033[1;43;33m"
|
||
bgwhite="\033[1;47;37m"
|
||
cl0="${reset}"
|
||
cl1="${magenta}"
|
||
cl2="${green}"
|
||
cl3="${white}"
|
||
cl4="${blue}"
|
||
cl5="${red}"
|
||
cl6="${yellow}"
|
||
cl7="${cyan}"
|
||
cl8="${black}"
|
||
cl9="${bgyellow}"
|
||
cl10="${bgwhite}"
|
||
color_blocks="on"
|
||
block_width=4
|
||
block_height=1
|
||
col_offset="auto"
|
||
|
||
##--------- Progress Bars
|
||
bar_char_elapsed="-"
|
||
bar_char_total="="
|
||
bar_border="on"
|
||
bar_length=15
|
||
bar_color_elapsed="distro"
|
||
bar_color_total="distro"
|
||
cpu_display="on"
|
||
memory_display="on"
|
||
battery_display="on"
|
||
disk_display="on"
|
||
|
||
##--------- Backend Settings
|
||
image_backend="ascii"
|
||
image_source="auto"
|
||
|
||
##--------- Ascii Options
|
||
ascii_distro="auto"
|
||
ascii_colors=(distro)
|
||
ascii_bold="on"
|
||
|
||
##--------- Image Options
|
||
image_loop="off"
|
||
thumbnail_dir="${XDG_CACHE_HOME:-${HOME}/.cache}/thumbnails/neofetch"
|
||
crop_mode="normal"
|
||
crop_offset="center"
|
||
image_size="auto"
|
||
gap=2
|
||
yoffset=0
|
||
xoffset=0
|
||
background_color=
|
||
|
||
##--------- Misc Options
|
||
stdout="off"
|
||
EOF
|
||
|
||
log_success "Конфигурация Neofetch установлена"
|
||
|
||
|
||
# === Настройка fail2ban ===
|
||
# Путь к файлу конфигурации
|
||
JAIL_FILE="/etc/fail2ban/jail.local"
|
||
|
||
# Создаём резервную копию, если файл уже существует
|
||
if [[ -f "$JAIL_FILE" ]]; then
|
||
cp "$JAIL_FILE" "${JAIL_FILE}.bak_$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
# Перезаписываем файл
|
||
cat > "$JAIL_FILE" << 'EOF'
|
||
[sshd]
|
||
enabled = true
|
||
port = 52515
|
||
filter = sshd
|
||
logpath = /var/log/auth.log
|
||
maxretry = 7
|
||
bantime = 1d
|
||
EOF
|
||
|
||
sudo systemctl restart fail2ban
|
||
sudo systemctl enable --now fail2ban
|
||
|
||
|
||
|
||
# === Финал ===
|
||
echo
|
||
log_success "🎉 Установка завершена!"
|
||
echo
|
||
log_info "📝 Важная информация:"
|
||
echo " - Пароли для NPM сохранены в: /opt/npm/.env"
|
||
echo " - SSH порт изменен на: 52515"
|
||
echo " - Docker установлен и настроен"
|
||
echo " - Zsh и плагины установлены"
|
||
echo
|
||
log_warning "⚠️ После перезагрузки оболочка Zsh будет установлена по умолчанию"
|
||
echo " и SSH будет доступен на порту 52515"
|
||
echo
|
||
log_info "🐳 Docker сети и сервисы готовы к использованию"
|
||
|
||
|
||
|
||
# === Переключение на Zsh ===
|
||
log_info "Переключаю оболочку пользователя на Zsh..."
|
||
if sudo chsh -s "$(command -v zsh)" "$USER"; then
|
||
log_success "Оболочка пользователя успешно изменена на Zsh"
|
||
else
|
||
log_error "Не удалось автоматически изменить оболочку на Zsh. Попробуйте вручную: chsh -s $(which zsh)"
|
||
fi
|
||
|
||
|
||
|
||
# === Проверка SSH ===
|
||
log_info "Проверяю, слушает ли SSH порт 52515..."
|
||
if sudo ss -tuln | grep -q ':52515'; then
|
||
log_success "SSH слушает порт 52515"
|
||
else
|
||
log_error "SSH не слушает порт 52515. Проверяю службу..."
|
||
sudo systemctl restart ssh || log_error "Не удалось перезапустить SSH"
|
||
fi
|
||
|
||
|
||
|
||
# === Проверка подключения по новому порту ===
|
||
log_info "Проверяю доступность SSH на порту 52515 локально..."
|
||
if nc -zv 127.0.0.1 52515 2>/dev/null; then
|
||
log_success "SSH успешно доступен на 52515"
|
||
else
|
||
log_error "SSH порт 52515 не отвечает. Проверьте ufw и конфиг /etc/ssh/sshd_config"
|
||
fi
|
||
|
||
|
||
|
||
# === Отключение и улучшение отправки к серверу ===
|
||
log_info "Переподключение файрвола..."
|
||
cp /etc/ufw/before.rules /etc/ufw/before.rules.bak_$(date +%F_%H-%M-%S) && cat > /etc/ufw/before.rules << 'EOF'
|
||
#
|
||
# rules.before
|
||
#
|
||
# Rules that should be run before the ufw command line added rules. Custom
|
||
# rules should be added to one of these chains:
|
||
# ufw-before-input
|
||
# ufw-before-output
|
||
# ufw-before-forward
|
||
#
|
||
|
||
# Don't delete these required lines, otherwise there will be errors
|
||
*filter
|
||
:ufw-before-input - [0:0]
|
||
:ufw-before-output - [0:0]
|
||
:ufw-before-forward - [0:0]
|
||
:ufw-not-local - [0:0]
|
||
# End required lines
|
||
|
||
|
||
# allow all on loopback
|
||
-A ufw-before-input -i lo -j ACCEPT
|
||
-A ufw-before-output -o lo -j ACCEPT
|
||
|
||
# quickly process packets for which we already have a connection
|
||
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||
-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||
-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||
|
||
# drop INVALID packets (logs these in loglevel medium and higher)
|
||
-A ufw-before-input -m conntrack --ctstate INVALID -j ufw-logging-deny
|
||
-A ufw-before-input -m conntrack --ctstate INVALID -j DROP
|
||
|
||
# ok icmp codes for INPUT
|
||
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j DROP
|
||
-A ufw-before-input -p icmp --icmp-type time-exceeded -j DROP
|
||
-A ufw-before-input -p icmp --icmp-type parameter-problem -j DROP
|
||
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP
|
||
-A ufw-before-input -p icmp --icmp-type source-quench -j DROP
|
||
|
||
# ok icmp code for FORWARD
|
||
-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j DROP
|
||
-A ufw-before-forward -p icmp --icmp-type time-exceeded -j DROP
|
||
-A ufw-before-forward -p icmp --icmp-type parameter-problem -j DROP
|
||
-A ufw-before-forward -p icmp --icmp-type echo-request -j DROP
|
||
|
||
# allow dhcp client to work
|
||
-A ufw-before-input -p udp --sport 67 --dport 68 -j ACCEPT
|
||
|
||
#
|
||
# ufw-not-local
|
||
#
|
||
-A ufw-before-input -j ufw-not-local
|
||
|
||
# if LOCAL, RETURN
|
||
-A ufw-not-local -m addrtype --dst-type LOCAL -j RETURN
|
||
|
||
# if MULTICAST, RETURN
|
||
-A ufw-not-local -m addrtype --dst-type MULTICAST -j RETURN
|
||
|
||
# if BROADCAST, RETURN
|
||
-A ufw-not-local -m addrtype --dst-type BROADCAST -j RETURN
|
||
|
||
# all other non-local packets are dropped
|
||
-A ufw-not-local -m limit --limit 3/min --limit-burst 10 -j ufw-logging-deny
|
||
-A ufw-not-local -j DROP
|
||
|
||
# allow MULTICAST mDNS for service discovery (be sure the MULTICAST line above
|
||
# is uncommented)
|
||
-A ufw-before-input -p udp -d 224.0.0.251 --dport 5353 -j ACCEPT
|
||
|
||
# allow MULTICAST UPnP for service discovery (be sure the MULTICAST line above
|
||
# is uncommented)
|
||
-A ufw-before-input -p udp -d 239.255.255.250 --dport 1900 -j ACCEPT
|
||
|
||
# don't delete the 'COMMIT' line or these rules won't be processed
|
||
COMMIT
|
||
EOF
|
||
|
||
|
||
|
||
# === Перезагрузка системы ===
|
||
log_warning "Система будет перезагружена через 10 секунд для применения изменений..."
|
||
for i in {10..1}; do
|
||
echo -ne "${YELLOW}⏳ Перезагрузка через ${i} секунд...${NC}\r"
|
||
sleep 1
|
||
done
|
||
echo
|
||
log_info "Перезагружаю систему..."
|
||
sudo reboot
|