dibuat api.sh, chmod +x, jalankan ./api.sh
#!/bin/bash
#===============================================================================
# SCRIPT INSTALLASI VPS DEBIAN 12 - VUE + RUST STACK
# Stack: Nginx + Certbot + Fail2Ban
# Frontend: VueJS (app.mydomain.com)
# Backend: Rust (api.mydomain.com)
# FIX: Rate limiting scope corrected for API config
#===============================================================================
set -e # Exit on error
# Warna untuk output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Logging function
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[!]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
log_step() { echo -e "${CYAN}>>>${NC} $1"; }
# Cek apakah dijalankan sebagai root
if [[ $EUID -ne 0 ]]; then
log_error "Script ini harus dijalankan sebagai root atau dengan sudo!"
exit 1
fi
# Cek OS
if [[ ! -f /etc/debian_version ]]; then
log_error "Script ini hanya untuk Debian!"
exit 1
fi
echo -e "${GREEN}
╔════════════════════════════════════════════════════════════╗
║ VPS DEBIAN 12 INSTALLER - VUE + RUST STACK ║
║ Nginx • Certbot • Fail2Ban ║
╚════════════════════════════════════════════════════════════╝
${NC}"
#===============================================================================
# BAGIAN 1: INPUT INTERAKTIF
#===============================================================================
log_step "KONFIGURASI DOMAIN & APLIKASI"
#-------------------------------------------------------------------------------
# 1.1 Domain Configuration
#-------------------------------------------------------------------------------
echo -e "\n${YELLOW}π KONFIGURASI DOMAIN${NC}"
# Frontend Domain
while true; do
read -p "Domain frontend (contoh: app.mydomain.com): " FRONTEND_DOMAIN
if [[ $FRONTEND_DOMAIN =~ ^[a-zA-Z0-9.-]+\.[a-z]{2,}$ ]]; then
break
else
log_warn "Format domain tidak valid. Coba lagi."
fi
done
# Backend Domain
while true; do
read -p "Domain backend API (contoh: api.mydomain.com): " BACKEND_DOMAIN
if [[ $BACKEND_DOMAIN =~ ^[a-zA-Z0-9.-]+\.[a-z]{2,}$ ]]; then
break
else
log_warn "Format domain tidak valid. Coba lagi."
fi
done
# Email untuk Let's Encrypt
read -p "Email untuk sertifikat SSL (opsional): " SSL_EMAIL
#-------------------------------------------------------------------------------
# 1.2 Rust Application Configuration
#-------------------------------------------------------------------------------
echo -e "\n${YELLOW}π¦ KONFIGURASI APLIKASI RUST${NC}"
read -p "Port internal aplikasi Rust [8080]: " RUST_PORT
RUST_PORT=${RUST_PORT:-8080}
read -p "Path binary Rust [/opt/apps/rust-backend/target/release/app]: " RUST_BINARY
RUST_BINARY=${RUST_BINARY:-/opt/apps/rust-backend/target/release/app}
read -p "Working directory Rust [/opt/apps/rust-backend]: " RUST_WORKDIR
RUST_WORKDIR=${RUST_WORKDIR:-/opt/apps/rust-backend}
#-------------------------------------------------------------------------------
# 1.3 Fail2Ban Configuration
#-------------------------------------------------------------------------------
echo -e "\n${YELLOW}π‘️ KONFIGURASI FAIL2BAN${NC}"
read -p "Max retry sebelum ban [5]: " F2B_MAXRETRY
F2B_MAXRETRY=${F2B_MAXRETRY:-5}
read -p "Ban time (contoh: 1h, 24h, 1w) [1h]: " F2B_BANTIME
F2B_BANTIME=${F2B_BANTIME:-1h}
read -p "Find time window (contoh: 10m, 30m) [10m]: " F2B_FINDTIME
F2B_FINDTIME=${F2B_FINDTIME:-10m}
#-------------------------------------------------------------------------------
# 1.4 Ringkasan & Konfirmasi
#-------------------------------------------------------------------------------
echo -e "\n${YELLOW}π RINGKASAN KONFIGURASI${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
printf "%-20s : %s\n" "Frontend Domain" "$FRONTEND_DOMAIN"
printf "%-20s : %s\n" "Backend Domain" "$BACKEND_DOMAIN"
printf "%-20s : %s\n" "SSL Email" "${SSL_EMAIL:-'(tidak diisi)'}"
printf "%-20s : %s\n" "Rust Port (internal)" "$RUST_PORT"
printf "%-20s : %s\n" "Fail2Ban Max Retry" "$F2B_MAXRETRY"
printf "%-20s : %s\n" "Fail2Ban Ban Time" "$F2B_BANTIME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
read -p "✅ Lanjutkan instalasi? (y/n): " CONFIRM
if [[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then
log_info "Instalasi dibatalkan oleh user."
exit 0
fi
#===============================================================================
# BAGIAN 2: UPDATE SISTEM & INSTALASI PACKAGE
#===============================================================================
log_step "UPDATE SISTEM & INSTALASI PACKAGE"
log_info "Memperbarui package list..."
apt update -qq
log_info "Melakukan upgrade sistem..."
apt upgrade -y -qq
log_info "Menginstal package yang diperlukan..."
apt install -y -qq \
nginx \
fail2ban \
certbot python3-certbot-nginx \
curl wget \
build-essential pkg-config libssl-dev
log_success "Semua package terinstal."
#===============================================================================
# BAGIAN 3: BUAT STRUKTUR DIREKTORI
#===============================================================================
log_step "MEMBUAT STRUKTUR DIREKTORI"
mkdir -p /var/www/app
mkdir -p /opt/apps/rust-backend
mkdir -p /etc/nginx/sites-available
mkdir -p /etc/nginx/sites-enabled
mkdir -p /etc/fail2ban/jail.d
mkdir -p /etc/fail2ban/filter.d
mkdir -p /var/log/nginx
# Hapus default nginx
rm -f /etc/nginx/sites-enabled/default
log_success "Struktur direktori siap."
#===============================================================================
# BAGIAN 4: KONFIGURASI NGINX - RATE LIMIT ZONE (FIXED)
#===============================================================================
log_step "KONFIGURASI RATE LIMITING ZONE"
# Fungsi untuk menambahkan limit_req_zone di dalam http block
add_rate_limit_zone() {
local nginx_conf="/etc/nginx/nginx.conf"
local zone_directive=" limit_req_zone \$binary_remote_addr zone=api_limit:10m rate=10r/s;"
# Cek apakah sudah ada
if grep -q "limit_req_zone.*api_limit" "$nginx_conf" 2>/dev/null; then
log_info "Rate limit zone 'api_limit' sudah terkonfigurasi."
return 0
fi
log_info "Menambahkan rate limit zone ke nginx.conf..."
# Backup konfigurasi
cp "$nginx_conf" "${nginx_conf}.bak.$(date +%Y%m%d%H%M%S)"
# Strategy 1: Inject setelah baris "http {" (paling aman)
if grep -q "^http[[:space:]]*{" "$nginx_conf"; then
# Gunakan awk untuk inject setelah "http {"
awk -v directive="$zone_directive" '
/^http[[:space:]]*\{/ {
print directive
next
}
{ print }
' "$nginx_conf" > "${nginx_conf}.tmp" && mv "${nginx_conf}.tmp" "$nginx_conf"
log_success "Rate limit zone ditambahkan setelah 'http {'."
return 0
fi
# Strategy 2: Inject sebelum "include /etc/nginx/conf.d/*.conf;" (biasanya di dalam http block)
if grep -q "include /etc/nginx/conf.d/\*.conf;" "$nginx_conf"; then
sed -i "/include \/etc\/nginx\/conf.d\/\*.conf;/i\\$zone_directive" "$nginx_conf"
log_success "Rate limit zone ditambahkan sebelum include conf.d."
return 0
fi
# Strategy 3: Inject sebelum "include /etc/nginx/sites-enabled/*;"
if grep -q "include /etc/nginx/sites-enabled/\*;" "$nginx_conf"; then
sed -i "/include \/etc\/nginx\/sites-enabled\/\*;/i\\$zone_directive" "$nginx_conf"
log_success "Rate limit zone ditambahkan sebelum include sites-enabled."
return 0
fi
# Fallback: Append ke akhir file (kemungkinan besar akan error, tapi sebagai last resort)
log_warn "Tidak menemukan posisi ideal, mencoba append ke akhir file..."
echo "" >> "$nginx_conf"
echo "# Rate limiting zone for API" >> "$nginx_conf"
echo "limit_req_zone \$binary_remote_addr zone=api_limit:10m rate=10r/s;" >> "$nginx_conf"
return 0
}
# Jalankan fungsi
add_rate_limit_zone
# Verifikasi konfigurasi nginx
log_info "Menguji konfigurasi Nginx..."
if nginx -t 2>&1 | grep -q "syntax is ok"; then
log_success "✓ Nginx configuration valid dengan rate limit zone."
else
log_error "✗ Nginx configuration test failed!"
nginx -t 2>&1 | head -5
log_warn "Restoring backup nginx.conf..."
# Restore backup terbaru
LATEST_BACKUP=$(ls -t /etc/nginx/nginx.conf.bak.* 2>/dev/null | head -1)
if [[ -n "$LATEST_BACKUP" ]]; then
cp "$LATEST_BACKUP" /etc/nginx/nginx.conf
log_info "Backup restored. Silakan cek manual konfigurasi nginx.conf Anda."
fi
exit 1
fi
log_success "Rate limit zone configured successfully."
#===============================================================================
# BAGIAN 5: KONFIGURASI NGINX - FRONTEND (VUEJS)
#===============================================================================
log_step "KONFIGURASI NGINX - FRONTEND ($FRONTEND_DOMAIN)"
cat > /etc/nginx/sites-available/${FRONTEND_DOMAIN} << EOF
# HTTP → HTTPS Redirect
server {
listen 80;
listen [::]:80;
server_name ${FRONTEND_DOMAIN};
return 301 https://\$server_name\$request_uri;
}
# HTTPS Server Block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ${FRONTEND_DOMAIN};
# SSL Certificate (diisi otomatis oleh Certbot)
ssl_certificate /etc/letsencrypt/live/${FRONTEND_DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${FRONTEND_DOMAIN}/privkey.pem;
# Modern SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
root /var/www/app;
index index.html;
# Logging
access_log /var/log/nginx/${FRONTEND_DOMAIN}-access.log;
error_log /var/log/nginx/${FRONTEND_DOMAIN}-error.log;
# Gzip Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript image/svg+xml;
# Vue Router History Mode Support
location / {
try_files \$uri \$uri/ /index.html;
}
# Cache Static Assets (1 year)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|otf|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
EOF
log_success "Konfigurasi frontend dibuat."
#===============================================================================
# BAGIAN 6: KONFIGURASI NGINX - BACKEND (RUST REVERSE PROXY) [FIXED]
#===============================================================================
log_step "KONFIGURASI NGINX - BACKEND ($BACKEND_DOMAIN)"
cat > /etc/nginx/sites-available/${BACKEND_DOMAIN} << EOF
# HTTP → HTTPS Redirect
server {
listen 80;
listen [::]:80;
server_name ${BACKEND_DOMAIN};
return 301 https://\$server_name\$request_uri;
}
# HTTPS + Reverse Proxy to Rust App
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ${BACKEND_DOMAIN};
# SSL Certificate
ssl_certificate /etc/letsencrypt/live/${BACKEND_DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${BACKEND_DOMAIN}/privkey.pem;
# Modern SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
# Logging
access_log /var/log/nginx/${BACKEND_DOMAIN}-access.log;
error_log /var/log/nginx/${BACKEND_DOMAIN}-error.log;
# ─────────────────────────────────────────
# LOCATION: Main API (dengan Rate Limiting)
# ─────────────────────────────────────────
location / {
# Rate Limiting: 10 req/detik, burst 20
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:${RUST_PORT};
proxy_http_version 1.1;
# WebSocket Support
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
# Standard Proxy Headers
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header X-Forwarded-Port \$server_port;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
# ─────────────────────────────────────────
# LOCATION: Health Check (TANPA Rate Limiting)
# ─────────────────────────────────────────
location /health {
# Tidak ada limit_req = tidak dibatasi
proxy_pass http://127.0.0.1:${RUST_PORT}/health;
access_log off;
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
}
# ─────────────────────────────────────────
# LOCATION: Metrics/Prometheus (Opsional, localhost only)
# ─────────────────────────────────────────
location /metrics {
proxy_pass http://127.0.0.1:${RUST_PORT}/metrics;
allow 127.0.0.1;
deny all;
access_log off;
}
# Security Headers for API
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
}
EOF
#===============================================================================
# BAGIAN 6.5: DUMMY SSL CERTIFICATES (TEMPORARY FIX)
#===============================================================================
log_step "MEMBUAT DUMMY SSL CERTIFICATES (TEMPORARY)"
create_dummy_cert() {
local domain=$1
local live_dir="/etc/letsencrypt/live/${domain}"
local archive_dir="/etc/letsencrypt/archive/${domain}"
mkdir -p "$live_dir" "$archive_dir"
if [[ ! -f "${live_dir}/privkey.pem" ]]; then
log_info "Generating dummy private key for ${domain}..."
openssl genrsa -out "${archive_dir}/privkey1.pem" 2048 2>/dev/null
chmod 600 "${archive_dir}/privkey1.pem"
ln -sf "${archive_dir}/privkey1.pem" "${live_dir}/privkey.pem"
fi
if [[ ! -f "${live_dir}/fullchain.pem" ]]; then
log_info "Generating dummy certificate for ${domain}..."
openssl req -new -x509 -key "${archive_dir}/privkey1.pem" \
-out "${archive_dir}/cert1.pem" \
-days 1 \
-subj "/CN=${domain}/O=Dummy/C=ID" \
2>/dev/null
ln -sf "${archive_dir}/cert1.pem" "${live_dir}/cert.pem"
ln -sf "${archive_dir}/cert1.pem" "${live_dir}/fullchain.pem"
ln -sf "${archive_dir}/cert1.pem" "${live_dir}/chain.pem"
fi
}
create_dummy_cert "${FRONTEND_DOMAIN}"
create_dummy_cert "${BACKEND_DOMAIN}"
log_success "Dummy SSL certificates ready."
# Aktifkan kedua situs
ln -sf /etc/nginx/sites-available/${FRONTEND_DOMAIN} /etc/nginx/sites-enabled/
ln -sf /etc/nginx/sites-available/${BACKEND_DOMAIN} /etc/nginx/sites-enabled/
# Test konfigurasi nginx
if nginx -t; then
log_success "Konfigurasi Nginx valid."
else
log_error "Konfigurasi Nginx gagal! Cek error di atas."
exit 1
fi
#===============================================================================
# BAGIAN 7: KONFIGURASI FAIL2BAN
#===============================================================================
log_step "KONFIGURASI FAIL2BAN"
#-------------------------------------------------------------------------------
# 7.1 Main Jail Configuration
#-------------------------------------------------------------------------------
log_info "Membuat konfigurasi jail Fail2Ban..."
cat > /etc/fail2ban/jail.d/nginx-custom.conf << EOF
[DEFAULT]
bantime = ${F2B_BANTIME}
findtime = ${F2B_FINDTIME}
maxretry = ${F2B_MAXRETRY}
backend = auto
ignoreip = 127.0.0.1/8 ::1
# Ban yang lebih lama untuk repeat offender
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 3
# 1. HTTP Auth Brute Force (jika ada basic auth di Nginx)
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/*error.log
# 2. Bot Scanning Protection (Frontend & Backend)
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/*access.log
maxretry = 2
bantime = 24h
# 3. Rate Limit Violations (API Abuse)
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/*error.log
maxretry = 10
bantime = 24h
# 4. API Specific Abuse (401/403 errors)
[nginx-api-abuse]
enabled = true
filter = nginx-api-abuse
port = http,https
logpath = /var/log/nginx/${BACKEND_DOMAIN}-access.log
maxretry = 20
bantime = 24h
# 5. Frontend Specific (Scanner/Probe detection)
[nginx-frontend-probe]
enabled = true
filter = nginx-frontend-probe
port = http,https
logpath = /var/log/nginx/${FRONTEND_DOMAIN}-access.log
maxretry = 5
bantime = 12h
EOF
log_success "Jail configuration dibuat."
#-------------------------------------------------------------------------------
# 7.2 Custom Filter: API Abuse
#-------------------------------------------------------------------------------
log_info "Membuat filter untuk API abuse detection..."
cat > /etc/fail2ban/filter.d/nginx-api-abuse.conf << 'EOF'
[Definition]
# Deteksi status code 401 (Unauthorized) atau 403 (Forbidden) di access log
failregex = ^<HOST> -.*"(GET|POST|PUT|DELETE|PATCH).*" (401|403)
ignoreregex =
EOF
log_success "Filter API abuse dibuat."
#-------------------------------------------------------------------------------
# 7.3 Custom Filter: Frontend Probe
#-------------------------------------------------------------------------------
log_info "Membuat filter untuk frontend probe detection..."
cat > /etc/fail2ban/filter.d/nginx-frontend-probe.conf << 'EOF'
[Definition]
# Deteksi scanning untuk file sensitif (.env, .git, wp-admin, phpmyadmin, dll)
failregex = ^<HOST> -.*"(GET|POST).*" 404.*(\.env|\.git|wp-admin|wp-login|phpmyadmin|\.sql|\.bak|\.config|admin|login|\.php|\.asp|\.aspx)
ignoreregex =
EOF
log_success "Filter frontend probe dibuat."
#-------------------------------------------------------------------------------
# 7.4 Enable & Restart Fail2Ban
#-------------------------------------------------------------------------------
log_info "Mengaktifkan dan merestart Fail2Ban..."
systemctl enable --now fail2ban
systemctl restart fail2ban
# Verifikasi
if systemctl is-active --quiet fail2ban; then
log_success "Fail2Ban aktif dan berjalan."
log_info "Jails yang aktif:"
fail2ban-client status | grep "Jail list" || true
else
log_warn "Fail2Ban gagal aktif. Cek dengan: systemctl status fail2ban"
fi
#===============================================================================
# BAGIAN 8: SYSTEMD SERVICE UNTUK RUST APPLICATION
#===============================================================================
log_step "MEMBUAT SYSTEMD SERVICE UNTUK RUST APP"
# Buat user khusus untuk aplikasi jika belum ada
if ! id -u rustapp >/dev/null 2>&1; then
useradd -r -m -d /opt/apps/rust-backend -s /usr/sbin/nologin rustapp 2>/dev/null || true
fi
cat > /etc/systemd/system/rust-api.service << EOF
[Unit]
Description=Rust Backend API Service
After=network.target
[Service]
Type=simple
User=rustapp
Group=rustapp
WorkingDirectory=${RUST_WORKDIR}
ExecStart=${RUST_BINARY}
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Environment Variables
Environment="RUST_LOG=info"
Environment="RUST_BACKTRACE=1"
Environment="PORT=${RUST_PORT}"
# Security Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=${RUST_WORKDIR} /var/log/nginx /tmp
# Resource Limits
LimitNOFILE=65535
MemoryMax=1G
[Install]
WantedBy=multi-user.target
EOF
# Set permission untuk binary dan directory
touch "${RUST_BINARY}" 2>/dev/null || true
chmod +x "${RUST_BINARY}" 2>/dev/null || true
chown -R rustapp:rustapp "${RUST_WORKDIR}" 2>/dev/null || true
log_success "Systemd service 'rust-api' dibuat."
#===============================================================================
# BAGIAN 9: CERTBOT / SSL CERTIFICATES
#===============================================================================
log_step "KONFIGURASI SSL DENGAN CERTBOT"
# Dapatkan IP server untuk info DNS
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}' || echo "YOUR_SERVER_IP")
echo -e "\n${YELLOW}π PENTING: DNS CONFIGURATION${NC}"
echo "Pastikan DNS record berikut sudah mengarah ke IP: ${CYAN}${SERVER_IP}${NC}"
echo " • ${FRONTEND_DOMAIN} → A record"
echo " • ${BACKEND_DOMAIN} → A record"
echo ""
read -p "Apakah DNS sudah siap dan ingin melanjutkan dengan Certbot? (y/n): " RUN_CERTBOT
if [[ "$RUN_CERTBOT" == "y" || "$RUN_CERTBOT" == "Y" ]]; then
log_info "Meminta sertifikat SSL dari Let's Encrypt..."
CERTBOT_ARGS="--nginx -d ${FRONTEND_DOMAIN} -d ${BACKEND_DOMAIN} --non-interactive --agree-tos --redirect"
[[ -n "$SSL_EMAIL" ]] && CERTBOT_ARGS="$CERTBOT_ARGS --email ${SSL_EMAIL}"
if certbot $CERTBOT_ARGS; then
log_success "✓ Sertifikat SSL berhasil dipasang!"
# Test auto-renew
log_info "Menguji auto-renew SSL (dry-run)..."
if certbot renew --dry-run --quiet; then
log_success "✓ Auto-renew SSL berfungsi."
else
log_warn "⚠ Auto-renew test gagal. Cek dengan: certbot renew --dry-run"
fi
# Reload nginx untuk apply SSL config
systemctl reload nginx
else
log_warn "⚠ Certbot gagal. Anda dapat menjalankannya manual nanti:"
echo " certbot --nginx -d ${FRONTEND_DOMAIN} -d ${BACKEND_DOMAIN}"
fi
else
log_warn "⚠ Certbot dilewati. Website akan accessible via HTTP saja sampai SSL dipasang manual."
fi
#===============================================================================
# BAGIAN 10: FINALISASI & RESTART SERVICES
#===============================================================================
log_step "FINALISASI INSTALASI"
log_info "Merestart dan enable services..."
systemctl daemon-reload
systemctl restart nginx
systemctl restart fail2ban
# Enable services di boot
systemctl enable nginx fail2ban rust-api 2>/dev/null || true
# Test konfigurasi akhir
log_info "Verifikasi konfigurasi..."
nginx -t >/dev/null 2>&1 && log_success "✓ Nginx config valid"
systemctl is-active --quiet fail2ban && log_success "✓ Fail2Ban running"
#===============================================================================
# BAGIAN 11: OUTPUT AKHIR & INSTRUKSI DEPLOY
#===============================================================================
echo -e "\n${GREEN}
╔════════════════════════════════════════════════════════════╗
║ π INSTALASI BERHASIL SELESAI! ║
╚════════════════════════════════════════════════════════════╝
${NC}"
echo -e "${YELLOW}π INFORMASI AKSES${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
printf "%-22s : %s\n" "Frontend URL" "https://${FRONTEND_DOMAIN}"
printf "%-22s : %s\n" "Backend URL" "https://${BACKEND_DOMAIN}"
printf "%-22s : %s\n" "SSH Command" "ssh user@${SERVER_IP}"
printf "%-22s : %s\n" "Rust Service" "systemctl status rust-api"
printf "%-22s : %s\n" "Rust Port (internal)" "${RUST_PORT}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e "\n${YELLOW}π‘️ FAIL2BAN STATUS${NC}"
echo "• Max Retry: ${F2B_MAXRETRY}"
echo "• Ban Time: ${F2B_BANTIME}"
echo "• Find Time: ${F2B_FINDTIME}"
echo ""
echo "Commands:"
echo " ${CYAN}fail2ban-client status${NC}"
echo " ${CYAN}fail2ban-client status nginx-api-abuse${NC}"
echo " ${CYAN}fail2ban-client status nginx-botsearch${NC}"
echo " ${CYAN}fail2ban-client unban <IP>${NC}"
echo -e "\n${YELLOW}π LANGKAH DEPLOY APLIKASI${NC}"
echo "
1️⃣ Upload Vue.js build ke frontend:
${CYAN}scp -r dist/* root@${SERVER_IP}:/var/www/app/${NC}
2️⃣ Upload Rust binary & config ke backend:
${CYAN}scp target/release/my_app root@${SERVER_IP}:${RUST_WORKDIR}/${NC}
3️⃣ Set permissions:
${CYAN}chown -R www-www-data /var/www/app${NC}
${CYAN}chown -R rustapp:rustapp ${RUST_WORKDIR}${NC}
${CYAN}chmod +x ${RUST_BINARY}${NC}
4️⃣ Start Rust application:
${CYAN}systemctl start rust-api${NC}
5️⃣ Verifikasi:
${CYAN}curl -I https://${FRONTEND_DOMAIN}${NC}
${CYAN}curl -I https://${BACKEND_DOMAIN}/health${NC}
"
echo -e "${YELLOW}π§ COMMANDS BERGUNA${NC}"
cat << 'EOF'
# Monitoring
tail -f /var/log/nginx/*-error.log
tail -f /var/log/nginx/*-access.log
journalctl -u rust-api -f
# Fail2Ban
fail2ban-client status
fail2ban-client status nginx-api-abuse
fail2ban-client unban <IP_ADDRESS>
fail2ban-client set nginx-api-abuse unbanip <IP_ADDRESS>
# SSL
certbot certificates
certbot renew --dry-run
# Nginx
nginx -t
systemctl reload nginx
systemctl status nginx
# View banned IPs
grep "Ban" /var/log/fail2ban.log | tail -20
# Test rate limiting
for i in {1..30}; do curl -s -o /dev/null -w "%{http_code}\n" https://${BACKEND_DOMAIN}/api/test; done
EOF
echo -e "\n${GREEN}Selamat! Stack Vue + Rust Anda siap produksi. π${NC}"
# Exit sukses
exit 0
No comments:
Post a Comment