Files
njomac 50c125e0db Initial commit — GuacPanel v1.0
All-in-one installer za Apache Guacamole s Nginx reverse proxy,
fail2ban, SSL (Let's Encrypt / Self-signed), GeoIP blokiranjem
i Flask web GUI za upravljanje.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 11:21:44 +02:00

738 lines
26 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# ================================================================
# Guacamole Management Script
# Upravljanje instalacijom nakon postavljanja
# ================================================================
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${GREEN}[✓]${NC} $*"; }
warn() { echo -e "${YELLOW}[⚠]${NC} $*"; }
error() { echo -e "${RED}[✗]${NC} $*" >&2; }
section() { echo -e "\n${BOLD}${BLUE}══════════ $* ══════════${NC}"; }
ok() { echo -e "${GREEN}$*${NC}"; }
[[ $EUID -ne 0 ]] && { echo -e "${RED}Pokrenite kao root: sudo bash $0${NC}"; exit 1; }
# ---------------------------------------------------------------
# Detekcija instalacije
# ---------------------------------------------------------------
INSTALL_DIR="/opt/guacamole"
GUAC_PORT="8080"
detect_nginx_conf() {
if [[ -f /etc/nginx/sites-available/guacamole ]]; then
echo "/etc/nginx/sites-available/guacamole"
elif [[ -f /etc/nginx/conf.d/guacamole.conf ]]; then
echo "/etc/nginx/conf.d/guacamole.conf"
else
echo ""
fi
}
detect_compose_cmd() {
if docker compose version &>/dev/null 2>&1; then
echo "docker compose"
elif command -v docker-compose &>/dev/null; then
echo "docker-compose"
else
echo ""
fi
}
NGINX_CONF=$(detect_nginx_conf)
COMPOSE_CMD=$(detect_compose_cmd)
[[ -z "$COMPOSE_CMD" ]] && { error "Docker Compose nije pronađen"; exit 1; }
# ---------------------------------------------------------------
# Pomoćne funkcije
# ---------------------------------------------------------------
nginx_reload() {
nginx -t && systemctl reload nginx
info "Nginx reloadiran"
}
get_domain() {
if [[ -n "$NGINX_CONF" ]]; then
grep -m1 "server_name" "$NGINX_CONF" | awk '{print $2}' | tr -d ';'
else
hostname -I | awk '{print $1}'
fi
}
pause() {
echo ""
read -rp "Pritisnite Enter za povratak u meni..."
}
confirm() {
read -rp "$(echo -e "${YELLOW}$* [y/N]: ${NC}")" ans
[[ "$ans" =~ ^[Yy]$ ]]
}
# ---------------------------------------------------------------
# Status pregled
# ---------------------------------------------------------------
show_status() {
section "Status servisa"
echo -e "\n${BOLD}Docker kontejneri:${NC}"
for c in guacamole guacd guac_postgres; do
if docker ps --filter "name=^/${c}$" --filter "status=running" | grep -q "$c" 2>/dev/null; then
echo -e " ${GREEN}${NC} $c — aktivan"
else
echo -e " ${RED}${NC} $c — NIJE aktivan"
fi
done
echo -e "\n${BOLD}Sistemski servisi:${NC}"
for svc in nginx fail2ban docker; do
if systemctl is-active --quiet "$svc" 2>/dev/null; then
echo -e " ${GREEN}${NC} $svc — aktivan"
else
echo -e " ${RED}${NC} $svc — NIJE aktivan"
fi
done
echo -e "\n${BOLD}SSL certifikat:${NC}"
local domain
domain=$(get_domain)
if certbot certificates 2>/dev/null | grep -q "$domain"; then
local expiry
expiry=$(certbot certificates 2>/dev/null | grep "Expiry Date" | head -1 | awk '{print $3, $4}')
echo -e " ${GREEN}${NC} Aktivan — ističe: $expiry"
else
echo -e " ${YELLOW}${NC} SSL nije postavljen"
fi
echo -e "\n${BOLD}fail2ban — zabanjeni IP-ovi:${NC}"
if systemctl is-active --quiet fail2ban 2>/dev/null; then
local banned
banned=$(fail2ban-client status nginx-basicauth 2>/dev/null | grep "Banned IP" | cut -d: -f2 | xargs)
if [[ -n "$banned" && "$banned" != " " ]]; then
echo -e " ${RED}$banned${NC}"
else
echo -e " ${GREEN}Nema zabanjenih IP-ova${NC}"
fi
else
echo -e " ${YELLOW}fail2ban nije aktivan${NC}"
fi
echo -e "\n${BOLD}Nginx konfiguracija:${NC} ${NGINX_CONF:-nije pronađena}"
echo -e "${BOLD}Instalacijski dir:${NC} ${INSTALL_DIR}"
echo -e "${BOLD}Domena:${NC} $(get_domain)"
pause
}
# ---------------------------------------------------------------
# Basic Auth upravljanje
# ---------------------------------------------------------------
menu_basicauth() {
while true; do
clear
section "Basic Auth upravljanje"
echo ""
echo " 1) Promijeni lozinku postojećeg korisnika"
echo " 2) Dodaj novog korisnika"
echo " 3) Ukloni korisnika"
echo " 4) Prikaži korisnike"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
echo ""
read -rp "Korisničko ime: " user
read -rsp "Nova lozinka: " pass; echo
if htpasswd -b /etc/nginx/.guac_htpasswd "$user" "$pass" 2>/dev/null; then
nginx_reload
info "Lozinka promijenjena za: $user"
else
error "Korisnik '$user' ne postoji. Dodaj ga opcijom 2."
fi
pause
;;
2)
echo ""
read -rp "Novo korisničko ime: " user
read -rsp "Lozinka: " pass; echo
htpasswd -b /etc/nginx/.guac_htpasswd "$user" "$pass"
nginx_reload
info "Korisnik dodan: $user"
pause
;;
3)
echo ""
read -rp "Korisničko ime za uklanjanje: " user
if confirm "Sigurno želite ukloniti korisnika '$user'?"; then
htpasswd -D /etc/nginx/.guac_htpasswd "$user"
nginx_reload
info "Korisnik uklonjen: $user"
fi
pause
;;
4)
echo ""
echo -e "${BOLD}Korisnici u /etc/nginx/.guac_htpasswd:${NC}"
cut -d: -f1 /etc/nginx/.guac_htpasswd 2>/dev/null | while read -r u; do
echo "$u"
done
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# SSL upravljanje
# ---------------------------------------------------------------
menu_ssl() {
while true; do
clear
section "SSL upravljanje"
echo ""
echo " 1) Omogući SSL (Let's Encrypt)"
echo " 2) Obnovi certifikat ručno"
echo " 3) Prikaži informacije o certifikatu"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
echo ""
local domain
domain=$(get_domain)
read -rp "Domena [${domain}]: " input_domain
domain="${input_domain:-$domain}"
read -rp "Email za Let's Encrypt: " email
[[ -z "$email" ]] && { warn "Email je obavezan"; pause; continue; }
if ! command -v certbot &>/dev/null; then
warn "Certbot nije instaliran. Instaliram..."
if command -v apt-get &>/dev/null; then
apt-get install -y certbot python3-certbot-nginx
elif command -v dnf &>/dev/null; then
dnf install -y certbot python3-certbot-nginx
elif command -v yum &>/dev/null; then
yum install -y certbot python3-certbot-nginx
else
error "Instalirajte certbot ručno"
pause; continue
fi
fi
certbot --nginx -d "$domain" --email "$email" \
--agree-tos --non-interactive --redirect
# Auto-obnova
if systemctl list-unit-files certbot.timer &>/dev/null; then
systemctl enable --now certbot.timer
else
grep -q "certbot renew" /etc/crontab 2>/dev/null || \
echo "0 3 * * * root certbot renew --quiet --post-hook 'systemctl reload nginx'" \
>> /etc/crontab
fi
nginx_reload
info "SSL aktivan za: $domain"
pause
;;
2)
certbot renew --force-renewal
systemctl reload nginx
info "Certifikat obnovljen"
pause
;;
3)
certbot certificates 2>/dev/null || warn "Nema instaliranih certifikata"
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# GeoIP upravljanje
# ---------------------------------------------------------------
menu_geoip() {
while true; do
clear
section "GeoIP upravljanje"
echo ""
echo " 1) Omogući GeoIP blokiranje"
echo " 2) Promijeni dozvoljene zemlje"
echo " 3) Onemogući GeoIP blokiranje"
echo " 4) Ažuriraj GeoIP bazu"
echo " 5) Prikaži trenutne postavke"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
echo ""
if [[ -f /etc/nginx/conf.d/guacamole-geoip.conf ]]; then
warn "GeoIP je već konfiguriran. Koristi opciju 2 za promjenu zemalja."
pause; continue
fi
warn "Potreban besplatni MaxMind račun: https://www.maxmind.com/en/geolite2/signup"
read -rp "MaxMind Account ID: " mm_id
read -rp "MaxMind License Key: " mm_key
read -rp "Dozvoljene zemlje (ISO kodovi, razmak, npr: HR BA SI): " countries
# Instalacija ovisno o distro
if command -v apt-get &>/dev/null; then
apt-get install -y libnginx-mod-http-geoip2 geoipupdate 2>/dev/null || \
warn "Neki paketi nisu instalirani — provjeri ručno"
elif command -v dnf &>/dev/null; then
dnf install -y nginx-mod-http-geoip2 geoipupdate 2>/dev/null || \
warn "Neki paketi nisu dostupni — provjeri ručno"
fi
# MaxMind konfiguracija
mkdir -p /usr/share/GeoIP
cat > /etc/GeoIP.conf << MMEOF
AccountID ${mm_id}
LicenseKey ${mm_key}
EditionIDs GeoLite2-Country
DatabaseDirectory /usr/share/GeoIP
MMEOF
geoipupdate && info "GeoIP baza preuzeta" || \
{ error "geoipupdate nije uspio — provjeri kredencijale"; pause; continue; }
_write_geoip_conf "$countries"
_apply_geoip_to_nginx
nginx_reload
info "GeoIP blokiranje aktivno — dozvoljene: $countries"
pause
;;
2)
echo ""
if [[ ! -f /etc/nginx/conf.d/guacamole-geoip.conf ]]; then
warn "GeoIP nije omogućen. Odaberi opciju 1."
pause; continue
fi
echo -e "Trenutne dozvoljene zemlje:"
grep -E "^\s+[A-Z]{2}\s+1;" /etc/nginx/conf.d/guacamole-geoip.conf \
| awk '{print " "$1}' | tr '\n' ' '
echo ""
read -rp "Nove dozvoljene zemlje (ISO kodovi, razmak): " countries
_write_geoip_conf "$countries"
nginx_reload
info "Ažurirane dozvoljene države: $countries"
pause
;;
3)
echo ""
if confirm "Sigurno želite onemogućiti GeoIP blokiranje?"; then
rm -f /etc/nginx/conf.d/guacamole-geoip.conf
# Ukloni GeoIP provjeru iz nginx location bloka
if [[ -n "$NGINX_CONF" ]]; then
sed -i '/allowed_country/d' "$NGINX_CONF"
fi
nginx_reload
info "GeoIP blokiranje onemogućeno"
fi
pause
;;
4)
if command -v geoipupdate &>/dev/null; then
geoipupdate && info "GeoIP baza ažurirana" || error "Ažuriranje nije uspjelo"
else
error "geoipupdate nije instaliran"
fi
pause
;;
5)
echo ""
if [[ -f /etc/nginx/conf.d/guacamole-geoip.conf ]]; then
echo -e "${BOLD}GeoIP status:${NC} ${GREEN}Aktivan${NC}"
echo -e "${BOLD}Dozvoljene zemlje:${NC}"
grep -E "^\s+[A-Z]{2}\s+1;" /etc/nginx/conf.d/guacamole-geoip.conf \
| awk '{print " "$1}' | tr '\n' ' '
echo ""
if [[ -f /usr/share/GeoIP/GeoLite2-Country.mmdb ]]; then
echo -e "${BOLD}GeoIP baza:${NC} $(stat -c '%y' /usr/share/GeoIP/GeoLite2-Country.mmdb | cut -d. -f1)"
fi
else
echo -e "${BOLD}GeoIP status:${NC} ${YELLOW}Nije aktivan${NC}"
fi
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
_write_geoip_conf() {
local countries="$1"
local entries=""
for cc in $countries; do
entries+=" ${cc} 1;\n"
done
cat > /etc/nginx/conf.d/guacamole-geoip.conf << EOF
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
\$geoip2_data_country_code country iso_code;
}
map \$geoip2_data_country_code \$allowed_country {
default 0;
$(echo -e "$entries")}
EOF
}
_apply_geoip_to_nginx() {
if [[ -n "$NGINX_CONF" ]]; then
# Ukloni staru provjeru ako postoji pa dodaj novu
sed -i '/allowed_country/d' "$NGINX_CONF"
sed -i '/auth_basic.*Restricted/i\ if ($allowed_country = 0) { return 403; }' "$NGINX_CONF"
fi
}
# ---------------------------------------------------------------
# fail2ban upravljanje
# ---------------------------------------------------------------
menu_fail2ban() {
while true; do
clear
section "fail2ban upravljanje"
echo ""
echo " 1) Prikaži zabanjene IP-ove"
echo " 2) Odbani IP adresu"
echo " 3) Ručno banaj IP adresu"
echo " 4) Promijeni broj pokušaja prije bana"
echo " 5) Promijeni trajanje bana"
echo " 6) Prikaži fail2ban log"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
echo ""
for jail in nginx-basicauth nginx-guacamole sshd; do
echo -e "${BOLD}[$jail]${NC}"
fail2ban-client status "$jail" 2>/dev/null \
| grep -E "Currently banned|Banned IP" || echo " jail nije aktivan"
echo ""
done
pause
;;
2)
echo ""
read -rp "IP adresa za odbanjivanje: " ip
for jail in nginx-basicauth nginx-guacamole sshd; do
fail2ban-client set "$jail" unbanip "$ip" 2>/dev/null && \
info "$ip odbanjivan iz $jail" || true
done
pause
;;
3)
echo ""
read -rp "IP adresa za baniranje: " ip
read -rp "Jail [nginx-basicauth]: " jail
jail="${jail:-nginx-basicauth}"
fail2ban-client set "$jail" banip "$ip"
info "$ip baniran u $jail"
pause
;;
4)
echo ""
local conf="/etc/fail2ban/jail.d/guacamole.conf"
current=$(grep -m1 "maxretry" "$conf" 2>/dev/null | awk '{print $3}')
read -rp "Broj pokušaja prije bana [trenutno: ${current:-5}]: " retries
retries="${retries:-${current:-5}}"
sed -i "s/^maxretry.*/maxretry = $retries/" "$conf"
systemctl restart fail2ban
info "maxretry postavljeno na: $retries"
pause
;;
5)
echo ""
local conf="/etc/fail2ban/jail.d/guacamole.conf"
current=$(grep -m1 "bantime" "$conf" 2>/dev/null | awk '{print $3}')
echo "Unesite trajanje u sekundama (3600=1h, 86400=1dan, 604800=1tjedan)"
read -rp "Trajanje bana [trenutno: ${current:-3600}s]: " bantime
bantime="${bantime:-${current:-3600}}"
sed -i "s/^bantime.*/bantime = $bantime/" "$conf"
systemctl restart fail2ban
info "bantime postavljeno na: ${bantime}s"
pause
;;
6)
echo ""
journalctl -u fail2ban -n 50 --no-pager | tail -50
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# Guacamole upravljanje
# ---------------------------------------------------------------
menu_guacamole() {
while true; do
clear
section "Guacamole upravljanje"
echo ""
echo " 1) Prikaži logove (live)"
echo " 2) Restart svih kontejnera"
echo " 3) Stop svih kontejnera"
echo " 4) Start svih kontejnera"
echo " 5) Ažuriraj Guacamole (pull nove image-e)"
echo " 6) Prikaži korištenje resursa (CPU/RAM)"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
echo -e "${YELLOW}Ctrl+C za izlaz${NC}"
cd "$INSTALL_DIR" && $COMPOSE_CMD logs -f --tail=100
;;
2)
cd "$INSTALL_DIR" && $COMPOSE_CMD restart
info "Kontejneri restartani"
pause
;;
3)
if confirm "Sigurno želite zaustaviti Guacamole?"; then
cd "$INSTALL_DIR" && $COMPOSE_CMD stop
info "Kontejneri zaustavljeni"
fi
pause
;;
4)
cd "$INSTALL_DIR" && $COMPOSE_CMD start
info "Kontejneri pokrenuti"
pause
;;
5)
echo ""
warn "Ovo će preuzeti najnovije Docker image-e i restartati servise."
if confirm "Nastaviti?"; then
cd "$INSTALL_DIR"
$COMPOSE_CMD pull
$COMPOSE_CMD up -d --force-recreate
info "Guacamole ažuriran"
fi
pause
;;
6)
docker stats guacamole guacd guac_postgres --no-stream
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# Nginx upravljanje
# ---------------------------------------------------------------
menu_nginx() {
while true; do
clear
section "Nginx upravljanje"
echo ""
echo " 1) Reload konfiguracije"
echo " 2) Test konfiguracije"
echo " 3) Prikaži nginx access log (live)"
echo " 4) Promijeni rate limit (zahtjevi/minuti)"
echo " 5) Prikaži aktivne konekcije"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
nginx_reload
pause
;;
2)
nginx -t
pause
;;
3)
echo -e "${YELLOW}Ctrl+C za izlaz${NC}"
tail -f /var/log/nginx/access.log
;;
4)
echo ""
local zones_conf="/etc/nginx/conf.d/guacamole-maps.conf"
current=$(grep "guac_req" "$zones_conf" 2>/dev/null | grep -oP '\d+(?=r/m)' | head -1)
read -rp "Rate limit zahtjevi/minuti [trenutno: ${current:-10}]: " rate
rate="${rate:-${current:-10}}"
sed -i "s/rate=[0-9]*r\/m rate=.*guac_req/rate=${rate}r\/m/" "$zones_conf" 2>/dev/null || \
sed -i "s/guac_req:10m rate=[0-9]*r\/m/guac_req:10m rate=${rate}r\/m/" "$zones_conf"
nginx_reload
info "Rate limit postavljen na: ${rate} req/min"
pause
;;
5)
echo ""
ss -tnp | grep nginx || echo "Nema aktivnih nginx konekcija"
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# Backup & restore
# ---------------------------------------------------------------
menu_backup() {
while true; do
clear
section "Backup & Restore"
echo ""
echo " 1) Napravi backup (konfiguracija + baza)"
echo " 2) Prikaži dostupne backupe"
echo " 3) Vrati backup"
echo " 0) Povratak"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1)
local ts backup_file
ts=$(date +%Y%m%d_%H%M%S)
backup_file="/root/guacamole_backup_${ts}.tar.gz"
echo ""
info "Backupiranje baze podataka..."
docker exec guac_postgres pg_dump -U guacamole_user guacamole_db \
> /tmp/guacamole_db_backup.sql
info "Arhiviranje konfiguracije..."
tar -czf "$backup_file" \
-C / \
--ignore-failed-read \
opt/guacamole \
etc/nginx/.guac_htpasswd \
etc/nginx/conf.d/guacamole-maps.conf \
etc/nginx/conf.d/guacamole-geoip.conf \
etc/nginx/sites-available/guacamole \
etc/nginx/conf.d/guacamole.conf \
etc/fail2ban/jail.d/guacamole.conf \
etc/fail2ban/filter.d/nginx-basicauth.conf \
etc/fail2ban/filter.d/nginx-guacamole.conf \
tmp/guacamole_db_backup.sql \
2>/dev/null || true
rm -f /tmp/guacamole_db_backup.sql
chmod 600 "$backup_file"
info "Backup kreiran: $backup_file"
echo -e "Veličina: $(du -sh "$backup_file" | cut -f1)"
pause
;;
2)
echo ""
if ls /root/guacamole_backup_*.tar.gz &>/dev/null; then
ls -lh /root/guacamole_backup_*.tar.gz
else
warn "Nema dostupnih backupa u /root/"
fi
pause
;;
3)
echo ""
if ! ls /root/guacamole_backup_*.tar.gz &>/dev/null; then
warn "Nema dostupnih backupa"
pause; continue
fi
ls -1 /root/guacamole_backup_*.tar.gz | nl
read -rp "Broj backupa za vraćanje: " num
local backup
backup=$(ls -1 /root/guacamole_backup_*.tar.gz | sed -n "${num}p")
[[ -z "$backup" ]] && { warn "Nevažeći odabir"; pause; continue; }
if confirm "Vraćanje: $backup — ovo će prepisati trenutne postavke. Nastaviti?"; then
tar -xzf "$backup" -C /
# Restore baze
if tar -tzf "$backup" | grep -q "guacamole_db_backup.sql"; then
docker exec -i guac_postgres psql -U guacamole_user guacamole_db \
< /tmp/guacamole_db_backup.sql
rm -f /tmp/guacamole_db_backup.sql
fi
nginx_reload
systemctl restart fail2ban
cd "$INSTALL_DIR" && $COMPOSE_CMD restart
info "Backup vraćen: $backup"
fi
pause
;;
0) return ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
# ---------------------------------------------------------------
# Glavni meni
# ---------------------------------------------------------------
main_menu() {
while true; do
clear
echo -e "${BOLD}${BLUE}"
echo " ╔═══════════════════════════════════════╗"
echo " ║ Guacamole Management Console ║"
echo " ╚═══════════════════════════════════════╝${NC}"
echo ""
echo -e " ${BOLD}1)${NC} Status servisa"
echo -e " ${BOLD}2)${NC} Basic Auth (korisnici)"
echo -e " ${BOLD}3)${NC} SSL certifikat"
echo -e " ${BOLD}4)${NC} GeoIP blokiranje"
echo -e " ${BOLD}5)${NC} fail2ban (banned IP-ovi)"
echo -e " ${BOLD}6)${NC} Guacamole (Docker kontejneri)"
echo -e " ${BOLD}7)${NC} Nginx"
echo -e " ${BOLD}8)${NC} Backup & Restore"
echo ""
echo -e " ${BOLD}0)${NC} Izlaz"
echo ""
read -rp "Odabir: " choice
case "$choice" in
1) show_status ;;
2) menu_basicauth ;;
3) menu_ssl ;;
4) menu_geoip ;;
5) menu_fail2ban ;;
6) menu_guacamole ;;
7) menu_nginx ;;
8) menu_backup ;;
0) echo ""; exit 0 ;;
*) warn "Nevažeći odabir" ;;
esac
done
}
main_menu