|
|
@@ -49,6 +49,10 @@ FALCONPROXY_BINARY="/usr/local/bin/falconproxy"
|
|
|
FALCONPROXY_CONFIG_FILE="$DB_DIR/falconproxy_config.conf"
|
|
|
LIMITER_SCRIPT="/usr/local/bin/firewallfalcon-limiter.sh"
|
|
|
LIMITER_SERVICE="/etc/systemd/system/firewallfalcon-limiter.service"
|
|
|
+BANDWIDTH_DIR="$DB_DIR/bandwidth"
|
|
|
+BANDWIDTH_SCRIPT="/usr/local/bin/firewallfalcon-bandwidth.sh"
|
|
|
+BANDWIDTH_SERVICE="/etc/systemd/system/firewallfalcon-bandwidth.service"
|
|
|
+TRIAL_CLEANUP_SCRIPT="/usr/local/bin/firewallfalcon-trial-cleanup.sh"
|
|
|
|
|
|
# --- ZiVPN Variables ---
|
|
|
ZIVPN_DIR="/etc/zivpn"
|
|
|
@@ -90,10 +94,17 @@ initial_setup() {
|
|
|
mkdir -p "$DB_DIR"
|
|
|
touch "$DB_FILE"
|
|
|
mkdir -p "$SSL_CERT_DIR"
|
|
|
+ mkdir -p "$BANDWIDTH_DIR"
|
|
|
|
|
|
echo -e "${C_BLUE}🔹 Configuring user limiter service...${C_RESET}"
|
|
|
setup_limiter_service
|
|
|
|
|
|
+ echo -e "${C_BLUE}🔹 Configuring bandwidth monitoring service...${C_RESET}"
|
|
|
+ setup_bandwidth_service
|
|
|
+
|
|
|
+ echo -e "${C_BLUE}🔹 Installing trial account cleanup script...${C_RESET}"
|
|
|
+ setup_trial_cleanup_script
|
|
|
+
|
|
|
if [ ! -f "$INSTALL_FLAG_FILE" ]; then
|
|
|
touch "$INSTALL_FLAG_FILE"
|
|
|
fi
|
|
|
@@ -211,13 +222,9 @@ while true; do
|
|
|
# Get count of sshd processes per user in one go is hard in bash without map,
|
|
|
# so we optimize the per-user check.
|
|
|
|
|
|
- while IFS=: read -r user pass expiry limit; do
|
|
|
+ while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
|
|
|
[[ -z "$user" || "$user" == \#* ]] && continue
|
|
|
|
|
|
- # 1. Active Check (Skip if user has no processes to save CPU)
|
|
|
- # pgrep -u is relatively cheap, but let's be smart.
|
|
|
- # If connection limit is huge, we might not care.
|
|
|
-
|
|
|
# --- Expiry Check ---
|
|
|
# Only check expiry if we have a valid expiry date
|
|
|
if [[ "$expiry" != "Never" && "$expiry" != "" ]]; then
|
|
|
@@ -284,6 +291,185 @@ EOF
|
|
|
}
|
|
|
|
|
|
|
|
|
+setup_bandwidth_service() {
|
|
|
+ mkdir -p "$BANDWIDTH_DIR"
|
|
|
+ cat > "$BANDWIDTH_SCRIPT" << 'BWEOF'
|
|
|
+#!/bin/bash
|
|
|
+DB_FILE="/etc/firewallfalcon/users.db"
|
|
|
+BW_DIR="/etc/firewallfalcon/bandwidth"
|
|
|
+
|
|
|
+mkdir -p "$BW_DIR"
|
|
|
+
|
|
|
+# Helper: ensure iptables chain exists for a user
|
|
|
+ensure_chain() {
|
|
|
+ local user="$1"
|
|
|
+ local uid
|
|
|
+ uid=$(id -u "$user" 2>/dev/null) || return 1
|
|
|
+ local chain="FF_BW_${user}"
|
|
|
+
|
|
|
+ if ! iptables -L "$chain" -n &>/dev/null; then
|
|
|
+ iptables -N "$chain" 2>/dev/null
|
|
|
+ iptables -A OUTPUT -m owner --uid-owner "$uid" -j "$chain" 2>/dev/null
|
|
|
+ iptables -A "$chain" -j RETURN 2>/dev/null
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+# Helper: read bytes from chain
|
|
|
+read_bytes() {
|
|
|
+ local user="$1"
|
|
|
+ local chain="FF_BW_${user}"
|
|
|
+ iptables -L "$chain" -vnx 2>/dev/null | awk '/RETURN/{print $2}' | head -1
|
|
|
+}
|
|
|
+
|
|
|
+# Helper: reset counter
|
|
|
+reset_counter() {
|
|
|
+ local user="$1"
|
|
|
+ local chain="FF_BW_${user}"
|
|
|
+ iptables -Z "$chain" 2>/dev/null
|
|
|
+}
|
|
|
+
|
|
|
+# Helper: remove chain for deleted user
|
|
|
+remove_chain() {
|
|
|
+ local user="$1"
|
|
|
+ local uid
|
|
|
+ uid=$(id -u "$user" 2>/dev/null)
|
|
|
+ local chain="FF_BW_${user}"
|
|
|
+
|
|
|
+ if iptables -L "$chain" -n &>/dev/null; then
|
|
|
+ if [[ -n "$uid" ]]; then
|
|
|
+ iptables -D OUTPUT -m owner --uid-owner "$uid" -j "$chain" 2>/dev/null
|
|
|
+ else
|
|
|
+ iptables -S OUTPUT 2>/dev/null | grep "$chain" | while read -r rule; do
|
|
|
+ iptables $(echo "$rule" | sed 's/^-A/-D/') 2>/dev/null
|
|
|
+ done
|
|
|
+ fi
|
|
|
+ iptables -F "$chain" 2>/dev/null
|
|
|
+ iptables -X "$chain" 2>/dev/null
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+while true; do
|
|
|
+ if [[ ! -f "$DB_FILE" ]]; then
|
|
|
+ sleep 60
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
|
|
|
+ [[ -z "$user" || "$user" == \#* ]] && continue
|
|
|
+
|
|
|
+ # Skip if no bandwidth limit (0 or empty = unlimited)
|
|
|
+ if [[ -z "$bandwidth_gb" || "$bandwidth_gb" == "0" ]]; then
|
|
|
+ remove_chain "$user" 2>/dev/null
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Skip if user doesn't exist on system
|
|
|
+ if ! id "$user" &>/dev/null; then continue; fi
|
|
|
+
|
|
|
+ # Ensure iptables chain exists
|
|
|
+ ensure_chain "$user"
|
|
|
+
|
|
|
+ # Read current counter bytes
|
|
|
+ current_bytes=$(read_bytes "$user")
|
|
|
+ [[ -z "$current_bytes" ]] && current_bytes=0
|
|
|
+
|
|
|
+ # Read accumulated usage
|
|
|
+ usage_file="$BW_DIR/${user}.usage"
|
|
|
+ accumulated=0
|
|
|
+ if [[ -f "$usage_file" ]]; then
|
|
|
+ accumulated=$(cat "$usage_file" 2>/dev/null)
|
|
|
+ [[ -z "$accumulated" ]] && accumulated=0
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Add current counter to accumulated
|
|
|
+ new_total=$((accumulated + current_bytes))
|
|
|
+ echo "$new_total" > "$usage_file"
|
|
|
+
|
|
|
+ # Reset iptables counter after reading
|
|
|
+ reset_counter "$user"
|
|
|
+
|
|
|
+ # Convert quota to bytes (1 GB = 1073741824 bytes)
|
|
|
+ quota_bytes=$(awk "BEGIN {printf \"%.0f\", $bandwidth_gb * 1073741824}")
|
|
|
+
|
|
|
+ # Lock user if over quota
|
|
|
+ if [[ "$new_total" -ge "$quota_bytes" ]]; then
|
|
|
+ if ! passwd -S "$user" 2>/dev/null | grep -q " L "; then
|
|
|
+ usermod -L "$user" &>/dev/null
|
|
|
+ killall -u "$user" -9 &>/dev/null
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ done < "$DB_FILE"
|
|
|
+
|
|
|
+ sleep 30
|
|
|
+done
|
|
|
+BWEOF
|
|
|
+ chmod +x "$BANDWIDTH_SCRIPT"
|
|
|
+
|
|
|
+ cat > "$BANDWIDTH_SERVICE" << EOF
|
|
|
+[Unit]
|
|
|
+Description=FirewallFalcon Bandwidth Monitor
|
|
|
+After=network.target
|
|
|
+
|
|
|
+[Service]
|
|
|
+Type=simple
|
|
|
+ExecStart=$BANDWIDTH_SCRIPT
|
|
|
+Restart=always
|
|
|
+RestartSec=5
|
|
|
+
|
|
|
+[Install]
|
|
|
+WantedBy=multi-user.target
|
|
|
+EOF
|
|
|
+
|
|
|
+ pkill -f "firewallfalcon-bandwidth" 2>/dev/null
|
|
|
+
|
|
|
+ if ! systemctl is-active --quiet firewallfalcon-bandwidth; then
|
|
|
+ systemctl daemon-reload
|
|
|
+ systemctl enable firewallfalcon-bandwidth &>/dev/null
|
|
|
+ systemctl start firewallfalcon-bandwidth --no-block &>/dev/null
|
|
|
+ else
|
|
|
+ systemctl restart firewallfalcon-bandwidth --no-block &>/dev/null
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+setup_trial_cleanup_script() {
|
|
|
+ cat > "$TRIAL_CLEANUP_SCRIPT" << 'TREOF'
|
|
|
+#!/bin/bash
|
|
|
+# FirewallFalcon Trial Account Auto-Cleanup
|
|
|
+# Usage: firewallfalcon-trial-cleanup.sh <username>
|
|
|
+DB_FILE="/etc/firewallfalcon/users.db"
|
|
|
+BW_DIR="/etc/firewallfalcon/bandwidth"
|
|
|
+
|
|
|
+username="$1"
|
|
|
+if [[ -z "$username" ]]; then exit 1; fi
|
|
|
+
|
|
|
+# Kill active sessions
|
|
|
+killall -u "$username" -9 &>/dev/null
|
|
|
+sleep 1
|
|
|
+
|
|
|
+# Remove iptables bandwidth chain if exists
|
|
|
+chain="FF_BW_${username}"
|
|
|
+uid=$(id -u "$username" 2>/dev/null)
|
|
|
+if iptables -L "$chain" -n &>/dev/null; then
|
|
|
+ if [[ -n "$uid" ]]; then
|
|
|
+ iptables -D OUTPUT -m owner --uid-owner "$uid" -j "$chain" 2>/dev/null
|
|
|
+ fi
|
|
|
+ iptables -F "$chain" 2>/dev/null
|
|
|
+ iptables -X "$chain" 2>/dev/null
|
|
|
+fi
|
|
|
+
|
|
|
+# Delete system user
|
|
|
+userdel -r "$username" &>/dev/null
|
|
|
+
|
|
|
+# Remove from DB
|
|
|
+sed -i "/^${username}:/d" "$DB_FILE"
|
|
|
+
|
|
|
+# Remove bandwidth tracking
|
|
|
+rm -f "$BW_DIR/${username}.usage"
|
|
|
+TREOF
|
|
|
+ chmod +x "$TRIAL_CLEANUP_SCRIPT"
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
generate_dns_record() {
|
|
|
echo -e "\n${C_BLUE}⚙️ Generating a random domain...${C_RESET}"
|
|
|
if ! command -v jq &> /dev/null; then
|
|
|
@@ -462,21 +648,30 @@ create_user() {
|
|
|
fi
|
|
|
local password=""
|
|
|
while true; do
|
|
|
- read -p "🔑 Enter new password: " password
|
|
|
+ read -p "🔑 Enter password (or press Enter for auto-generated): " password
|
|
|
if [[ -z "$password" ]]; then
|
|
|
- echo -e "${C_RED}❌ Password cannot be empty. Please try again.${C_RESET}"
|
|
|
+ password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
|
|
|
+ echo -e "${C_GREEN}🔑 Auto-generated password: ${C_YELLOW}$password${C_RESET}"
|
|
|
+ break
|
|
|
else
|
|
|
break
|
|
|
fi
|
|
|
done
|
|
|
read -p "🗓️ Enter account duration (in days): " days
|
|
|
if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
- read -p "📶 Enter simultaneous connection limit: " limit
|
|
|
+ read -p "📶 Enter simultaneous connection limit [1]: " limit
|
|
|
+ limit=${limit:-1}
|
|
|
if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+ read -p "📦 Enter bandwidth limit in GB (0 = unlimited) [0]: " bandwidth_gb
|
|
|
+ bandwidth_gb=${bandwidth_gb:-0}
|
|
|
+ if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
local expire_date
|
|
|
expire_date=$(date -d "+$days days" +%Y-%m-%d)
|
|
|
useradd -m -s /usr/sbin/nologin "$username"; echo "$username:$password" | chpasswd; chage -E "$expire_date" "$username"
|
|
|
- echo "$username:$password:$expire_date:$limit" >> "$DB_FILE"
|
|
|
+ echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
|
|
|
+
|
|
|
+ local bw_display="Unlimited"
|
|
|
+ if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
|
|
|
|
|
|
clear; show_banner
|
|
|
echo -e "${C_GREEN}✅ User '$username' created successfully!${C_RESET}\n"
|
|
|
@@ -484,7 +679,8 @@ create_user() {
|
|
|
echo -e " - 🔑 Password: ${C_YELLOW}$password${C_RESET}"
|
|
|
echo -e " - 🗓️ Expires on: ${C_YELLOW}$expire_date${C_RESET}"
|
|
|
echo -e " - 📶 Connection Limit: ${C_YELLOW}$limit${C_RESET}"
|
|
|
- echo -e " ${C_DIM}(Active monitoring service will enforce this limit)${C_RESET}"
|
|
|
+ echo -e " - 📦 Bandwidth Limit: ${C_YELLOW}$bw_display${C_RESET}"
|
|
|
+ echo -e " ${C_DIM}(Active monitoring service will enforce these limits)${C_RESET}"
|
|
|
|
|
|
# Auto-ask for config generation
|
|
|
echo
|
|
|
@@ -538,6 +734,19 @@ delete_user() {
|
|
|
echo -e "\n${C_RED}❌ Failed to delete system user '$username'.${C_RESET}"
|
|
|
fi
|
|
|
|
|
|
+ # Clean up bandwidth tracking
|
|
|
+ local chain="FF_BW_${username}"
|
|
|
+ if iptables -L "$chain" -n &>/dev/null; then
|
|
|
+ local uid_val
|
|
|
+ uid_val=$(id -u "$username" 2>/dev/null)
|
|
|
+ if [[ -n "$uid_val" ]]; then
|
|
|
+ iptables -D OUTPUT -m owner --uid-owner "$uid_val" -j "$chain" 2>/dev/null
|
|
|
+ fi
|
|
|
+ iptables -F "$chain" 2>/dev/null
|
|
|
+ iptables -X "$chain" 2>/dev/null
|
|
|
+ fi
|
|
|
+ rm -f "$BANDWIDTH_DIR/${username}.usage"
|
|
|
+
|
|
|
sed -i "/^$username:/d" "$DB_FILE"
|
|
|
echo -e "${C_GREEN}✅ User '$username' has been completely removed.${C_RESET}"
|
|
|
}
|
|
|
@@ -548,41 +757,78 @@ edit_user() {
|
|
|
if [[ "$username" == "NO_USERS" ]] || [[ -z "$username" ]]; then return; fi
|
|
|
while true; do
|
|
|
clear; show_banner; echo -e "${C_BOLD}${C_PURPLE}--- Editing User: ${C_YELLOW}$username${C_PURPLE} ---${C_RESET}"
|
|
|
+
|
|
|
+ # Show current user details
|
|
|
+ local current_line; current_line=$(grep "^$username:" "$DB_FILE")
|
|
|
+ local cur_pass; cur_pass=$(echo "$current_line" | cut -d: -f2)
|
|
|
+ local cur_expiry; cur_expiry=$(echo "$current_line" | cut -d: -f3)
|
|
|
+ local cur_limit; cur_limit=$(echo "$current_line" | cut -d: -f4)
|
|
|
+ local cur_bw; cur_bw=$(echo "$current_line" | cut -d: -f5)
|
|
|
+ [[ -z "$cur_bw" ]] && cur_bw="0"
|
|
|
+ local cur_bw_display="Unlimited"; [[ "$cur_bw" != "0" ]] && cur_bw_display="${cur_bw} GB"
|
|
|
+
|
|
|
+ # Show bandwidth usage
|
|
|
+ local bw_used_display="N/A"
|
|
|
+ if [[ -f "$BANDWIDTH_DIR/${username}.usage" ]]; then
|
|
|
+ local used_bytes; used_bytes=$(cat "$BANDWIDTH_DIR/${username}.usage" 2>/dev/null)
|
|
|
+ if [[ -n "$used_bytes" && "$used_bytes" != "0" ]]; then
|
|
|
+ bw_used_display=$(awk "BEGIN {printf \"%.2f GB\", $used_bytes / 1073741824}")
|
|
|
+ else
|
|
|
+ bw_used_display="0.00 GB"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo -e "\n ${C_DIM}Current: Pass=${C_YELLOW}$cur_pass${C_RESET}${C_DIM} Exp=${C_YELLOW}$cur_expiry${C_RESET}${C_DIM} Conn=${C_YELLOW}$cur_limit${C_RESET}${C_DIM} BW=${C_YELLOW}$cur_bw_display${C_RESET}${C_DIM} Used=${C_CYAN}$bw_used_display${C_RESET}"
|
|
|
echo -e "\nSelect a detail to edit:\n"
|
|
|
printf " ${C_GREEN}[ 1]${C_RESET} %-35s\n" "🔑 Change Password"
|
|
|
printf " ${C_GREEN}[ 2]${C_RESET} %-35s\n" "🗓️ Change Expiration Date"
|
|
|
printf " ${C_GREEN}[ 3]${C_RESET} %-35s\n" "📶 Change Connection Limit"
|
|
|
+ printf " ${C_GREEN}[ 4]${C_RESET} %-35s\n" "📦 Change Bandwidth Limit"
|
|
|
+ printf " ${C_GREEN}[ 5]${C_RESET} %-35s\n" "🔄 Reset Bandwidth Counter"
|
|
|
echo -e "\n ${C_RED}[ 0]${C_RESET} ✅ Finish Editing"; echo; read -p "👉 Enter your choice: " edit_choice
|
|
|
case $edit_choice in
|
|
|
1)
|
|
|
local new_pass=""
|
|
|
- while true; do
|
|
|
- read -p "Enter new password: " new_pass
|
|
|
- if [[ -z "$new_pass" ]]; then
|
|
|
- echo -e "${C_RED}❌ Password cannot be empty. Please try again.${C_RESET}"
|
|
|
- else
|
|
|
- break
|
|
|
- fi
|
|
|
- done
|
|
|
+ read -p "Enter new password (or press Enter for auto-generated): " new_pass
|
|
|
+ if [[ -z "$new_pass" ]]; then
|
|
|
+ new_pass=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
|
|
|
+ echo -e "${C_GREEN}🔑 Auto-generated: ${C_YELLOW}$new_pass${C_RESET}"
|
|
|
+ fi
|
|
|
echo "$username:$new_pass" | chpasswd
|
|
|
- local current_line; current_line=$(grep "^$username:" "$DB_FILE"); local expiry; expiry=$(echo "$current_line" | cut -d: -f3); local limit; limit=$(echo "$current_line" | cut -d: -f4)
|
|
|
- sed -i "s/^$username:.*/$username:$new_pass:$expiry:$limit/" "$DB_FILE"
|
|
|
- echo -e "\n${C_GREEN}✅ Password for '$username' changed successfully.${C_RESET}"
|
|
|
- echo -e "New Password: ${C_YELLOW}$new_pass${C_RESET}"
|
|
|
+ sed -i "s/^$username:.*/$username:$new_pass:$cur_expiry:$cur_limit:$cur_bw/" "$DB_FILE"
|
|
|
+ echo -e "\n${C_GREEN}✅ Password for '$username' changed to: ${C_YELLOW}$new_pass${C_RESET}"
|
|
|
;;
|
|
|
2) read -p "Enter new duration (in days from today): " days
|
|
|
if [[ "$days" =~ ^[0-9]+$ ]]; then
|
|
|
local new_expire_date; new_expire_date=$(date -d "+$days days" +%Y-%m-%d); chage -E "$new_expire_date" "$username"
|
|
|
- local current_line; current_line=$(grep "^$username:" "$DB_FILE"); local pass; pass=$(echo "$current_line" | cut -d: -f2); local limit; limit=$(echo "$current_line" | cut -d: -f4)
|
|
|
- sed -i "s/^$username:.*/$username:$pass:$new_expire_date:$limit/" "$DB_FILE"
|
|
|
+ sed -i "s/^$username:.*/$username:$cur_pass:$new_expire_date:$cur_limit:$cur_bw/" "$DB_FILE"
|
|
|
echo -e "\n${C_GREEN}✅ Expiration for '$username' set to ${C_YELLOW}$new_expire_date${C_RESET}."
|
|
|
else echo -e "\n${C_RED}❌ Invalid number of days.${C_RESET}"; fi ;;
|
|
|
3) read -p "Enter new simultaneous connection limit: " new_limit
|
|
|
if [[ "$new_limit" =~ ^[0-9]+$ ]]; then
|
|
|
- local current_line; current_line=$(grep "^$username:" "$DB_FILE"); local pass; pass=$(echo "$current_line" | cut -d: -f2); local expiry; expiry=$(echo "$current_line" | cut -d: -f3)
|
|
|
- sed -i "s/^$username:.*/$username:$pass:$expiry:$new_limit/" "$DB_FILE"
|
|
|
+ sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$new_limit:$cur_bw/" "$DB_FILE"
|
|
|
echo -e "\n${C_GREEN}✅ Connection limit for '$username' set to ${C_YELLOW}$new_limit${C_RESET}."
|
|
|
else echo -e "\n${C_RED}❌ Invalid limit.${C_RESET}"; fi ;;
|
|
|
+ 4) read -p "Enter new bandwidth limit in GB (0 = unlimited): " new_bw
|
|
|
+ if [[ "$new_bw" =~ ^[0-9]+\.?[0-9]*$ ]]; then
|
|
|
+ sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$cur_limit:$new_bw/" "$DB_FILE"
|
|
|
+ local bw_msg="Unlimited"; [[ "$new_bw" != "0" ]] && bw_msg="${new_bw} GB"
|
|
|
+ echo -e "\n${C_GREEN}✅ Bandwidth limit for '$username' set to ${C_YELLOW}$bw_msg${C_RESET}."
|
|
|
+ # Unlock user if they were locked due to bandwidth
|
|
|
+ if [[ "$new_bw" == "0" ]] || [[ -f "$BANDWIDTH_DIR/${username}.usage" ]]; then
|
|
|
+ local used_bytes; used_bytes=$(cat "$BANDWIDTH_DIR/${username}.usage" 2>/dev/null || echo 0)
|
|
|
+ local new_quota_bytes; new_quota_bytes=$(awk "BEGIN {printf \"%.0f\", $new_bw * 1073741824}")
|
|
|
+ if [[ "$new_bw" == "0" ]] || [[ "$used_bytes" -lt "$new_quota_bytes" ]]; then
|
|
|
+ usermod -U "$username" &>/dev/null
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ else echo -e "\n${C_RED}❌ Invalid bandwidth value.${C_RESET}"; fi ;;
|
|
|
+ 5)
|
|
|
+ echo "0" > "$BANDWIDTH_DIR/${username}.usage"
|
|
|
+ # Unlock user if they were locked due to bandwidth
|
|
|
+ usermod -U "$username" &>/dev/null
|
|
|
+ echo -e "\n${C_GREEN}✅ Bandwidth counter for '$username' has been reset to 0.${C_RESET}"
|
|
|
+ ;;
|
|
|
0) return ;;
|
|
|
*) echo -e "\n${C_RED}❌ Invalid option.${C_RESET}" ;;
|
|
|
esac
|
|
|
@@ -668,11 +914,11 @@ list_users() {
|
|
|
return
|
|
|
fi
|
|
|
echo -e "${C_BOLD}${C_PURPLE}--- 📋 Managed Users ---${C_RESET}"
|
|
|
- echo -e "${C_CYAN}======================================================================${C_RESET}"
|
|
|
- printf "${C_BOLD}${C_WHITE}%-20s | %-12s | %-15s | %-20s${C_RESET}\n" "USERNAME" "EXPIRES" "CONNECTIONS" "STATUS"
|
|
|
- echo -e "${C_CYAN}----------------------------------------------------------------------${C_RESET}"
|
|
|
+ echo -e "${C_CYAN}=========================================================================================${C_RESET}"
|
|
|
+ printf "${C_BOLD}${C_WHITE}%-18s | %-12s | %-10s | %-15s | %-20s${C_RESET}\n" "USERNAME" "EXPIRES" "CONNS" "BANDWIDTH" "STATUS"
|
|
|
+ echo -e "${C_CYAN}-----------------------------------------------------------------------------------------${C_RESET}"
|
|
|
|
|
|
- while IFS=: read -r user pass expiry limit; do
|
|
|
+ while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
|
|
|
local online_count
|
|
|
online_count=$(pgrep -u "$user" sshd | wc -l)
|
|
|
|
|
|
@@ -683,6 +929,20 @@ list_users() {
|
|
|
plain_status=$(echo -e "$status" | sed 's/\x1b\[[0-9;]*m//g')
|
|
|
|
|
|
local connection_string="$online_count / $limit"
|
|
|
+
|
|
|
+ # Bandwidth display
|
|
|
+ [[ -z "$bandwidth_gb" ]] && bandwidth_gb="0"
|
|
|
+ local bw_string="Unlimited"
|
|
|
+ if [[ "$bandwidth_gb" != "0" ]]; then
|
|
|
+ local used_bytes=0
|
|
|
+ if [[ -f "$BANDWIDTH_DIR/${user}.usage" ]]; then
|
|
|
+ used_bytes=$(cat "$BANDWIDTH_DIR/${user}.usage" 2>/dev/null)
|
|
|
+ [[ -z "$used_bytes" ]] && used_bytes=0
|
|
|
+ fi
|
|
|
+ local used_gb
|
|
|
+ used_gb=$(awk "BEGIN {printf \"%.1f\", $used_bytes / 1073741824}")
|
|
|
+ bw_string="${used_gb}/${bandwidth_gb}GB"
|
|
|
+ fi
|
|
|
|
|
|
local line_color="$C_WHITE"
|
|
|
case $plain_status in
|
|
|
@@ -692,17 +952,18 @@ list_users() {
|
|
|
*"Not Found"*) line_color="$C_DIM" ;;
|
|
|
esac
|
|
|
|
|
|
- printf "${line_color}%-20s ${C_RESET}| ${C_YELLOW}%-12s ${C_RESET}| ${C_CYAN}%-15s ${C_RESET}| %-20s\n" "$user" "$expiry" "$connection_string" "$status"
|
|
|
+ printf "${line_color}%-18s ${C_RESET}| ${C_YELLOW}%-12s ${C_RESET}| ${C_CYAN}%-10s ${C_RESET}| ${C_ORANGE}%-15s ${C_RESET}| %-20s\n" "$user" "$expiry" "$connection_string" "$bw_string" "$status"
|
|
|
done < <(sort "$DB_FILE")
|
|
|
- echo -e "${C_CYAN}======================================================================${C_RESET}\n"
|
|
|
+ echo -e "${C_CYAN}=========================================================================================${C_RESET}\n"
|
|
|
}
|
|
|
|
|
|
renew_user() {
|
|
|
_select_user_interface "--- 🔄 Renew a User ---"; local u=$SELECTED_USER; if [[ "$u" == "NO_USERS" || -z "$u" ]]; then return; fi
|
|
|
read -p "👉 Enter number of days to extend the account: " days; if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
local new_expire_date; new_expire_date=$(date -d "+$days days" +%Y-%m-%d); chage -E "$new_expire_date" "$u"
|
|
|
- local line; line=$(grep "^$u:" "$DB_FILE"); local pass; pass=$(echo "$line"|cut -d: -f2); local limit; limit=$(echo "$line"|cut -d: -f4)
|
|
|
- sed -i "s/^$u:.*/$u:$pass:$new_expire_date:$limit/" "$DB_FILE"
|
|
|
+ local line; line=$(grep "^$u:" "$DB_FILE"); local pass; pass=$(echo "$line"|cut -d: -f2); local limit; limit=$(echo "$line"|cut -d: -f4); local bw; bw=$(echo "$line"|cut -d: -f5)
|
|
|
+ [[ -z "$bw" ]] && bw="0"
|
|
|
+ sed -i "s/^$u:.*/$u:$pass:$new_expire_date:$limit:$bw/" "$DB_FILE"
|
|
|
echo -e "\n${C_GREEN}✅ User '$u' has been renewed. New expiration date is ${C_YELLOW}${new_expire_date}${C_RESET}."
|
|
|
}
|
|
|
|
|
|
@@ -719,7 +980,7 @@ cleanup_expired() {
|
|
|
return
|
|
|
fi
|
|
|
|
|
|
- while IFS=: read -r user pass expiry limit; do
|
|
|
+ while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
|
|
|
local expiry_ts
|
|
|
expiry_ts=$(date -d "$expiry" +%s 2>/dev/null || echo 0)
|
|
|
|
|
|
@@ -740,6 +1001,15 @@ cleanup_expired() {
|
|
|
for user in "${expired_users[@]}"; do
|
|
|
echo " - Deleting ${C_YELLOW}$user...${C_RESET}"
|
|
|
killall -u "$user" -9 &>/dev/null
|
|
|
+ # Clean up bandwidth chain
|
|
|
+ local chain="FF_BW_${user}"
|
|
|
+ local uid_val; uid_val=$(id -u "$user" 2>/dev/null)
|
|
|
+ if iptables -L "$chain" -n &>/dev/null; then
|
|
|
+ [[ -n "$uid_val" ]] && iptables -D OUTPUT -m owner --uid-owner "$uid_val" -j "$chain" 2>/dev/null
|
|
|
+ iptables -F "$chain" 2>/dev/null
|
|
|
+ iptables -X "$chain" 2>/dev/null
|
|
|
+ fi
|
|
|
+ rm -f "$BANDWIDTH_DIR/${user}.usage"
|
|
|
userdel -r "$user" &>/dev/null
|
|
|
sed -i "/^$user:/d" "$DB_FILE"
|
|
|
done
|
|
|
@@ -2235,7 +2505,7 @@ show_banner() {
|
|
|
|
|
|
clear
|
|
|
echo
|
|
|
- echo -e "${C_TITLE} FirewallFalcon Manager ${C_RESET}${C_DIM}| v3.5.0 Premium Edition${C_RESET}"
|
|
|
+ echo -e "${C_TITLE} FirewallFalcon Manager ${C_RESET}${C_DIM}| v4.0.0 Premium Edition${C_RESET}"
|
|
|
echo -e "${C_BLUE} ─────────────────────────────────────────────────────────${C_RESET}"
|
|
|
printf " ${C_GRAY}%-10s${C_RESET} %-20s ${C_GRAY}|${C_RESET} %s\n" "OS" "$os_name" "Uptime: $up_time"
|
|
|
printf " ${C_GRAY}%-10s${C_RESET} %-20s ${C_GRAY}|${C_RESET} %s\n" "Memory" "${ram_usage}% Used" "Online Sessions: ${C_WHITE}${online_users}${C_RESET}"
|
|
|
@@ -2439,6 +2709,22 @@ uninstall_script() {
|
|
|
rm -f "$LIMITER_SERVICE"
|
|
|
rm -f "$LIMITER_SCRIPT"
|
|
|
|
|
|
+ echo -e "\n${C_BLUE}🗑️ Removing bandwidth monitoring service...${C_RESET}"
|
|
|
+ systemctl stop firewallfalcon-bandwidth &>/dev/null
|
|
|
+ systemctl disable firewallfalcon-bandwidth &>/dev/null
|
|
|
+ rm -f "$BANDWIDTH_SERVICE"
|
|
|
+ rm -f "$BANDWIDTH_SCRIPT"
|
|
|
+ rm -f "$TRIAL_CLEANUP_SCRIPT"
|
|
|
+
|
|
|
+ # Clean up iptables bandwidth chains
|
|
|
+ iptables -S OUTPUT 2>/dev/null | grep 'FF_BW_' | while read -r rule; do
|
|
|
+ iptables $(echo "$rule" | sed 's/^-A/-D/') 2>/dev/null
|
|
|
+ done
|
|
|
+ iptables -L 2>/dev/null | grep 'Chain FF_BW_' | awk '{print $2}' | while read -r chain; do
|
|
|
+ iptables -F "$chain" 2>/dev/null
|
|
|
+ iptables -X "$chain" 2>/dev/null
|
|
|
+ done
|
|
|
+
|
|
|
chattr -i /etc/resolv.conf &>/dev/null
|
|
|
|
|
|
purge_nginx "silent"
|
|
|
@@ -2469,6 +2755,240 @@ uninstall_script() {
|
|
|
|
|
|
# --- NEW FEATURES ---
|
|
|
|
|
|
+create_trial_account() {
|
|
|
+ clear; show_banner
|
|
|
+ echo -e "${C_BOLD}${C_PURPLE}--- ⏱️ Create Trial/Test Account ---${C_RESET}"
|
|
|
+
|
|
|
+ # Ensure 'at' daemon is available
|
|
|
+ if ! command -v at &>/dev/null; then
|
|
|
+ echo -e "${C_YELLOW}⚠️ 'at' command not found. Installing...${C_RESET}"
|
|
|
+ apt-get update > /dev/null 2>&1 && apt-get install -y at || {
|
|
|
+ echo -e "${C_RED}❌ Failed to install 'at'. Cannot schedule auto-expiry.${C_RESET}"
|
|
|
+ return
|
|
|
+ }
|
|
|
+ systemctl enable atd &>/dev/null
|
|
|
+ systemctl start atd &>/dev/null
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Ensure atd is running
|
|
|
+ if ! systemctl is-active --quiet atd; then
|
|
|
+ systemctl start atd &>/dev/null
|
|
|
+ fi
|
|
|
+
|
|
|
+ echo -e "\n${C_CYAN}Select trial duration:${C_RESET}\n"
|
|
|
+ printf " ${C_GREEN}[ 1]${C_RESET} ⏱️ 1 Hour\n"
|
|
|
+ printf " ${C_GREEN}[ 2]${C_RESET} ⏱️ 2 Hours\n"
|
|
|
+ printf " ${C_GREEN}[ 3]${C_RESET} ⏱️ 3 Hours\n"
|
|
|
+ printf " ${C_GREEN}[ 4]${C_RESET} ⏱️ 6 Hours\n"
|
|
|
+ printf " ${C_GREEN}[ 5]${C_RESET} ⏱️ 12 Hours\n"
|
|
|
+ printf " ${C_GREEN}[ 6]${C_RESET} 📅 1 Day\n"
|
|
|
+ printf " ${C_GREEN}[ 7]${C_RESET} 📅 3 Days\n"
|
|
|
+ printf " ${C_GREEN}[ 8]${C_RESET} ⚙️ Custom (enter hours)\n"
|
|
|
+ echo -e "\n ${C_RED}[ 0]${C_RESET} ↩️ Cancel"
|
|
|
+ echo
|
|
|
+ read -p "👉 Select duration: " dur_choice
|
|
|
+
|
|
|
+ local duration_hours=0
|
|
|
+ local duration_label=""
|
|
|
+ case $dur_choice in
|
|
|
+ 1) duration_hours=1; duration_label="1 Hour" ;;
|
|
|
+ 2) duration_hours=2; duration_label="2 Hours" ;;
|
|
|
+ 3) duration_hours=3; duration_label="3 Hours" ;;
|
|
|
+ 4) duration_hours=6; duration_label="6 Hours" ;;
|
|
|
+ 5) duration_hours=12; duration_label="12 Hours" ;;
|
|
|
+ 6) duration_hours=24; duration_label="1 Day" ;;
|
|
|
+ 7) duration_hours=72; duration_label="3 Days" ;;
|
|
|
+ 8) read -p "👉 Enter custom duration in hours: " custom_hours
|
|
|
+ if ! [[ "$custom_hours" =~ ^[0-9]+$ ]] || [[ "$custom_hours" -lt 1 ]]; then
|
|
|
+ echo -e "\n${C_RED}❌ Invalid number of hours.${C_RESET}"; return
|
|
|
+ fi
|
|
|
+ duration_hours=$custom_hours
|
|
|
+ duration_label="$custom_hours Hours"
|
|
|
+ ;;
|
|
|
+ 0) echo -e "\n${C_YELLOW}❌ Cancelled.${C_RESET}"; return ;;
|
|
|
+ *) echo -e "\n${C_RED}❌ Invalid option.${C_RESET}"; return ;;
|
|
|
+ esac
|
|
|
+
|
|
|
+ # Username
|
|
|
+ local rand_suffix=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 5)
|
|
|
+ local default_username="trial_${rand_suffix}"
|
|
|
+ read -p "👤 Username [${default_username}]: " username
|
|
|
+ username=${username:-$default_username}
|
|
|
+
|
|
|
+ if id "$username" &>/dev/null || grep -q "^$username:" "$DB_FILE"; then
|
|
|
+ echo -e "\n${C_RED}❌ Error: User '$username' already exists.${C_RESET}"; return
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Password
|
|
|
+ local password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
|
|
|
+ read -p "🔑 Password [${password}]: " custom_pass
|
|
|
+ password=${custom_pass:-$password}
|
|
|
+
|
|
|
+ # Connection limit
|
|
|
+ read -p "📶 Connection limit [1]: " limit
|
|
|
+ limit=${limit:-1}
|
|
|
+ if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ # Bandwidth limit
|
|
|
+ read -p "📦 Bandwidth limit in GB (0 = unlimited) [0]: " bandwidth_gb
|
|
|
+ bandwidth_gb=${bandwidth_gb:-0}
|
|
|
+ if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ # Calculate expiry
|
|
|
+ local expire_date
|
|
|
+ if [[ "$duration_hours" -ge 24 ]]; then
|
|
|
+ local days=$((duration_hours / 24))
|
|
|
+ expire_date=$(date -d "+$days days" +%Y-%m-%d)
|
|
|
+ else
|
|
|
+ # For sub-day durations, set expiry to tomorrow to be safe (at job does the real cleanup)
|
|
|
+ expire_date=$(date -d "+1 day" +%Y-%m-%d)
|
|
|
+ fi
|
|
|
+ local expiry_timestamp
|
|
|
+ expiry_timestamp=$(date -d "+${duration_hours} hours" '+%Y-%m-%d %H:%M:%S')
|
|
|
+
|
|
|
+ # Create the system user
|
|
|
+ useradd -m -s /usr/sbin/nologin "$username"
|
|
|
+ echo "$username:$password" | chpasswd
|
|
|
+ chage -E "$expire_date" "$username"
|
|
|
+ echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
|
|
|
+
|
|
|
+ # Schedule auto-cleanup via 'at'
|
|
|
+ echo "$TRIAL_CLEANUP_SCRIPT $username" | at now + ${duration_hours} hours 2>/dev/null
|
|
|
+
|
|
|
+ local bw_display="Unlimited"
|
|
|
+ if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
|
|
|
+
|
|
|
+ clear; show_banner
|
|
|
+ echo -e "${C_GREEN}✅ Trial account created successfully!${C_RESET}\n"
|
|
|
+ echo -e "${C_YELLOW}========================================${C_RESET}"
|
|
|
+ echo -e " ⏱️ ${C_BOLD}TRIAL ACCOUNT${C_RESET}"
|
|
|
+ echo -e "${C_YELLOW}========================================${C_RESET}"
|
|
|
+ echo -e " - 👤 Username: ${C_YELLOW}$username${C_RESET}"
|
|
|
+ echo -e " - 🔑 Password: ${C_YELLOW}$password${C_RESET}"
|
|
|
+ echo -e " - ⏱️ Duration: ${C_CYAN}$duration_label${C_RESET}"
|
|
|
+ echo -e " - 🕐 Auto-expires at: ${C_RED}$expiry_timestamp${C_RESET}"
|
|
|
+ echo -e " - 📶 Connection Limit: ${C_YELLOW}$limit${C_RESET}"
|
|
|
+ echo -e " - 📦 Bandwidth Limit: ${C_YELLOW}$bw_display${C_RESET}"
|
|
|
+ echo -e "${C_YELLOW}========================================${C_RESET}"
|
|
|
+ echo -e "\n${C_DIM}The account will be automatically deleted when the trial expires.${C_RESET}"
|
|
|
+
|
|
|
+ # Auto-ask for config generation
|
|
|
+ echo
|
|
|
+ read -p "👉 Generate client config for this trial user? (y/n): " gen_conf
|
|
|
+ if [[ "$gen_conf" == "y" || "$gen_conf" == "Y" ]]; then
|
|
|
+ generate_client_config "$username" "$password"
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+view_user_bandwidth() {
|
|
|
+ _select_user_interface "--- 📊 View User Bandwidth ---"
|
|
|
+ local u=$SELECTED_USER
|
|
|
+ if [[ "$u" == "NO_USERS" || -z "$u" ]]; then return; fi
|
|
|
+
|
|
|
+ clear; show_banner
|
|
|
+ echo -e "${C_BOLD}${C_PURPLE}--- 📊 Bandwidth Details: ${C_YELLOW}$u${C_PURPLE} ---${C_RESET}\n"
|
|
|
+
|
|
|
+ local line; line=$(grep "^$u:" "$DB_FILE")
|
|
|
+ local bandwidth_gb; bandwidth_gb=$(echo "$line" | cut -d: -f5)
|
|
|
+ [[ -z "$bandwidth_gb" ]] && bandwidth_gb="0"
|
|
|
+
|
|
|
+ local used_bytes=0
|
|
|
+ if [[ -f "$BANDWIDTH_DIR/${u}.usage" ]]; then
|
|
|
+ used_bytes=$(cat "$BANDWIDTH_DIR/${u}.usage" 2>/dev/null)
|
|
|
+ [[ -z "$used_bytes" ]] && used_bytes=0
|
|
|
+ fi
|
|
|
+
|
|
|
+ local used_mb; used_mb=$(awk "BEGIN {printf \"%.2f\", $used_bytes / 1048576}")
|
|
|
+ local used_gb; used_gb=$(awk "BEGIN {printf \"%.3f\", $used_bytes / 1073741824}")
|
|
|
+
|
|
|
+ echo -e " ${C_CYAN}Data Used:${C_RESET} ${C_WHITE}${used_gb} GB${C_RESET} (${used_mb} MB)"
|
|
|
+
|
|
|
+ if [[ "$bandwidth_gb" == "0" ]]; then
|
|
|
+ echo -e " ${C_CYAN}Bandwidth Limit:${C_RESET} ${C_GREEN}Unlimited${C_RESET}"
|
|
|
+ echo -e " ${C_CYAN}Status:${C_RESET} ${C_GREEN}No quota restrictions${C_RESET}"
|
|
|
+ else
|
|
|
+ local quota_bytes; quota_bytes=$(awk "BEGIN {printf \"%.0f\", $bandwidth_gb * 1073741824}")
|
|
|
+ local percentage; percentage=$(awk "BEGIN {printf \"%.1f\", ($used_bytes / $quota_bytes) * 100}")
|
|
|
+ local remaining_bytes; remaining_bytes=$((quota_bytes - used_bytes))
|
|
|
+ if [[ "$remaining_bytes" -lt 0 ]]; then remaining_bytes=0; fi
|
|
|
+ local remaining_gb; remaining_gb=$(awk "BEGIN {printf \"%.3f\", $remaining_bytes / 1073741824}")
|
|
|
+
|
|
|
+ echo -e " ${C_CYAN}Bandwidth Limit:${C_RESET} ${C_YELLOW}${bandwidth_gb} GB${C_RESET}"
|
|
|
+ echo -e " ${C_CYAN}Remaining:${C_RESET} ${C_WHITE}${remaining_gb} GB${C_RESET}"
|
|
|
+ echo -e " ${C_CYAN}Usage:${C_RESET} ${C_WHITE}${percentage}%${C_RESET}"
|
|
|
+
|
|
|
+ # Progress bar
|
|
|
+ local bar_width=30
|
|
|
+ local filled; filled=$(awk "BEGIN {printf \"%.0f\", ($percentage / 100) * $bar_width}")
|
|
|
+ if [[ "$filled" -gt "$bar_width" ]]; then filled=$bar_width; fi
|
|
|
+ local empty=$((bar_width - filled))
|
|
|
+ local bar_color="$C_GREEN"
|
|
|
+ if (( $(awk "BEGIN {print ($percentage > 80)}" ) )); then bar_color="$C_RED"
|
|
|
+ elif (( $(awk "BEGIN {print ($percentage > 50)}" ) )); then bar_color="$C_YELLOW"
|
|
|
+ fi
|
|
|
+ printf " ${C_CYAN}Progress:${C_RESET} ${bar_color}["
|
|
|
+ for ((i=0; i<filled; i++)); do printf "█"; done
|
|
|
+ for ((i=0; i<empty; i++)); do printf "░"; done
|
|
|
+ printf "]${C_RESET} ${percentage}%%\n"
|
|
|
+
|
|
|
+ if [[ "$used_bytes" -ge "$quota_bytes" ]]; then
|
|
|
+ echo -e "\n ${C_RED}⚠️ USER HAS EXCEEDED BANDWIDTH QUOTA — ACCOUNT LOCKED${C_RESET}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+bulk_create_users() {
|
|
|
+ clear; show_banner
|
|
|
+ echo -e "${C_BOLD}${C_PURPLE}--- 👥 Bulk Create Users ---${C_RESET}"
|
|
|
+
|
|
|
+ read -p "👉 Enter username prefix (e.g., 'user'): " prefix
|
|
|
+ if [[ -z "$prefix" ]]; then echo -e "\n${C_RED}❌ Prefix cannot be empty.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ read -p "🔢 How many users to create? " count
|
|
|
+ if ! [[ "$count" =~ ^[0-9]+$ ]] || [[ "$count" -lt 1 ]] || [[ "$count" -gt 100 ]]; then
|
|
|
+ echo -e "\n${C_RED}❌ Invalid count (1-100).${C_RESET}"; return
|
|
|
+ fi
|
|
|
+
|
|
|
+ read -p "🗓️ Account duration (in days): " days
|
|
|
+ if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ read -p "📶 Connection limit per user [1]: " limit
|
|
|
+ limit=${limit:-1}
|
|
|
+ if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ read -p "📦 Bandwidth limit in GB per user (0 = unlimited) [0]: " bandwidth_gb
|
|
|
+ bandwidth_gb=${bandwidth_gb:-0}
|
|
|
+ if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
|
|
|
+
|
|
|
+ local expire_date
|
|
|
+ expire_date=$(date -d "+$days days" +%Y-%m-%d)
|
|
|
+ local bw_display="Unlimited"; [[ "$bandwidth_gb" != "0" ]] && bw_display="${bandwidth_gb} GB"
|
|
|
+
|
|
|
+ echo -e "\n${C_BLUE}⚙️ Creating $count users with prefix '${prefix}'...${C_RESET}\n"
|
|
|
+ echo -e "${C_YELLOW}================================================================${C_RESET}"
|
|
|
+ printf "${C_BOLD}${C_WHITE}%-20s | %-15s | %-12s${C_RESET}\n" "USERNAME" "PASSWORD" "EXPIRES"
|
|
|
+ echo -e "${C_YELLOW}----------------------------------------------------------------${C_RESET}"
|
|
|
+
|
|
|
+ local created=0
|
|
|
+ for ((i=1; i<=count; i++)); do
|
|
|
+ local username="${prefix}${i}"
|
|
|
+ if id "$username" &>/dev/null || grep -q "^$username:" "$DB_FILE"; then
|
|
|
+ echo -e "${C_RED} ⚠️ Skipping '$username' — already exists${C_RESET}"
|
|
|
+ continue
|
|
|
+ fi
|
|
|
+ local password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
|
|
|
+ useradd -m -s /usr/sbin/nologin "$username"
|
|
|
+ echo "$username:$password" | chpasswd
|
|
|
+ chage -E "$expire_date" "$username"
|
|
|
+ echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
|
|
|
+ printf " ${C_GREEN}%-20s${C_RESET} | ${C_YELLOW}%-15s${C_RESET} | ${C_CYAN}%-12s${C_RESET}\n" "$username" "$password" "$expire_date"
|
|
|
+ created=$((created + 1))
|
|
|
+ done
|
|
|
+
|
|
|
+ echo -e "${C_YELLOW}================================================================${C_RESET}"
|
|
|
+ echo -e "\n${C_GREEN}✅ Created $created users. Conn Limit: ${limit} | BW: ${bw_display}${C_RESET}"
|
|
|
+}
|
|
|
+
|
|
|
generate_client_config() {
|
|
|
local user=$1
|
|
|
local pass=$2
|
|
|
@@ -2817,22 +3337,24 @@ main_menu() {
|
|
|
show_banner
|
|
|
|
|
|
echo
|
|
|
- echo -e " ${C_TITLE}═══════════════[ ${C_BOLD}👤 USER MANAGEMENT ${C_RESET}${C_TITLE}]═══════════════${C_RESET}"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "1" "Create New User" "5" "Unlock User Account"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "2" "Delete User" "6" "Edit User Details"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "3" "Renew User Account" "7" "List Managed Users"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "4" "Lock User Account" "8" "Generate Client Config"
|
|
|
+ echo -e " ${C_TITLE}═══════════════════[ ${C_BOLD}👤 USER MANAGEMENT ${C_RESET}${C_TITLE}]═══════════════════${C_RESET}"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "1" "✨ Create New User" "6" "✏️ Edit User Details"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "2" "🗑️ Delete User" "7" "📋 List Managed Users"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "3" "🔄 Renew User Account" "8" "📱 Generate Client Config"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "4" "🔒 Lock User Account" "9" "⏱️ Create Trial Account"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "5" "🔓 Unlock User Account" "10" "📊 View User Bandwidth"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "11" "👥 Bulk Create Users"
|
|
|
|
|
|
echo
|
|
|
- echo -e " ${C_TITLE}════════════[ ${C_BOLD}🌐 VPN & PROTOCOLS ${C_RESET}${C_TITLE}]═════════════${C_RESET}"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "9" "Protocol Manager" "11" "Traffic Monitor (Lite)"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "10" "DT Proxy Manager" "12" "Block Torrent (Anti-P2P)"
|
|
|
+ echo -e " ${C_TITLE}══════════════[ ${C_BOLD}🌐 VPN & PROTOCOLS ${C_RESET}${C_TITLE}]═══════════════${C_RESET}"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "12" "🔌 Protocol Manager" "14" "📈 Traffic Monitor (Lite)"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "13" "DT Proxy Manager" "15" "🚫 Block Torrent (Anti-P2P)"
|
|
|
|
|
|
echo
|
|
|
- echo -e " ${C_TITLE}════════════[ ${C_BOLD}⚙️ SYSTEM SETTINGS ${C_RESET}${C_TITLE}]═════════════${C_RESET}"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "13" "CloudFlare Free Domain" "16" "Backup User Data"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "14" "SSH Banner Config" "17" "Restore User Data"
|
|
|
- printf " ${C_CHOICE}[%2s]${C_RESET} %-25s ${C_CHOICE}[%2s]${C_RESET} %-25s\n" "15" "Auto-Reboot Task" "18" "Cleanup Expired Users"
|
|
|
+ echo -e " ${C_TITLE}══════════════[ ${C_BOLD}⚙️ SYSTEM SETTINGS ${C_RESET}${C_TITLE}]═══════════════${C_RESET}"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "16" "☁️ CloudFlare Free Domain" "19" "💾 Backup User Data"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "17" "🎨 SSH Banner Config" "20" "📥 Restore User Data"
|
|
|
+ printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "18" "🔄 Auto-Reboot Task" "21" "🧹 Cleanup Expired Users"
|
|
|
|
|
|
echo
|
|
|
echo -e " ${C_DANGER}═══════════════════[ ${C_BOLD}🔥 DANGER ZONE ${C_RESET}${C_DANGER}]═══════════════════${C_RESET}"
|
|
|
@@ -2848,18 +3370,21 @@ main_menu() {
|
|
|
6) edit_user; press_enter ;;
|
|
|
7) list_users; press_enter ;;
|
|
|
8) client_config_menu; press_enter ;;
|
|
|
+ 9) create_trial_account; press_enter ;;
|
|
|
+ 10) view_user_bandwidth; press_enter ;;
|
|
|
+ 11) bulk_create_users; press_enter ;;
|
|
|
|
|
|
- 9) protocol_menu ;;
|
|
|
- 10) dt_proxy_menu ;;
|
|
|
- 11) traffic_monitor_menu ;;
|
|
|
- 12) torrent_block_menu ;;
|
|
|
+ 12) protocol_menu ;;
|
|
|
+ 13) dt_proxy_menu ;;
|
|
|
+ 14) traffic_monitor_menu ;;
|
|
|
+ 15) torrent_block_menu ;;
|
|
|
|
|
|
- 13) dns_menu; press_enter ;;
|
|
|
- 14) ssh_banner_menu ;;
|
|
|
- 15) auto_reboot_menu ;;
|
|
|
- 16) backup_user_data; press_enter ;;
|
|
|
- 17) restore_user_data; press_enter ;;
|
|
|
- 18) cleanup_expired; press_enter ;;
|
|
|
+ 16) dns_menu; press_enter ;;
|
|
|
+ 17) ssh_banner_menu ;;
|
|
|
+ 18) auto_reboot_menu ;;
|
|
|
+ 19) backup_user_data; press_enter ;;
|
|
|
+ 20) restore_user_data; press_enter ;;
|
|
|
+ 21) cleanup_expired; press_enter ;;
|
|
|
|
|
|
99) uninstall_script ;;
|
|
|
0) exit 0 ;;
|