Sistem Operasi Ubuntu Server 20.04 lts s/d Ubuntu Server 24.04 lts
Instal conntrack (jika belum ada). Ini adalah alat penting untuk memutus koneksi secara paksa.
sudo apt update && sudo apt install conntrack -y
Pastikan tool kalkulasi (bc) terinstall (biasanya sudah ada, tapi ini untuk jaga-jaga):
sudo apt install bc -y
Jalan kan perintah berikut :
sudo mkdir -p /opt/udp-proxy/bin
sudo mkdir -p /opt/udp-proxy/log
Langkah 2: Membuat Skrip Utama Penjaga
1.Buat file skrip:
sudo nano /opt/udp-proxy/bin/enforcer_service.sh
2.Salin dan tempel seluruh skrip di bawah ini.
#!/bin/bash
# =================================================================
# --- CEK FILE BENDERA (PANEL KONTROL) ---
# =================================================================
FLAG_FILE="/opt/udp-proxy/log/enforcer.enabled"
if [ ! -f "$FLAG_FILE" ]; then
echo "[$(date)] File bendera '$FLAG_FILE' tidak ditemukan. Penjaga tidak aktif. Keluar."
exit 0
fi
# =================================================================
# --- KONFIGURASI PENJAGA ---
# =================================================================
PROTECTED_PORT=36712
STATE_FILE="/opt/udp-proxy/log/enforcer_state.log"
IPTABLES_CHAIN="UDP_ENFORCER"
BANDWIDTH_LOG="/opt/udp-proxy/log/bandwidth_usage.log"
MEMORY_MINUTES=10
TIMEOUT_MINUTES=15
GRACE_PERIOD_SECONDS=0
UDP_SERVICE_NAME="udp-custom.service"
BANDWIDTH_CHAIN="UDP_BW_COUNTER"
FLUSH_QUEUE="/opt/udp-proxy/log/bw_flush_queue.log"
ACCUMULATOR_FILE="/opt/udp-proxy/log/bw_accumulator.log"
HISTORY_1H_FILE="/opt/udp-proxy/log/bw_history_1h.log"
### [NEW] Buku Harian 24 Jam (Tidak pernah terhapus sebelum jam 00:00) ###
DAILY_LOG_DIR="/opt/udp-proxy/log/daily"
DAILY_STATE="/opt/udp-proxy/log/daily_state.log"
DAILY_LEDGER="/opt/udp-proxy/log/bw_daily_ledger.log"
# =================================================================
mkdir -p "$(dirname "$STATE_FILE")"
mkdir -p "$(dirname "$BANDWIDTH_LOG")"
mkdir -p "$DAILY_LOG_DIR"
touch "$STATE_FILE"
touch "$BANDWIDTH_LOG"
touch "$FLUSH_QUEUE"
touch "$ACCUMULATOR_FILE"
touch "$HISTORY_1H_FILE"
touch "$DAILY_STATE"
touch "$DAILY_LEDGER"
# =================================================================
# --- FUNGSI PEMBERSIH ---
# =================================================================
function cleanup() {
echo "[$(date)] =====> MEMULAI PEMBERSIHAN DAN EKSEKUSI TIMER <====="
local current_time=$(date +%s)
local timeout_seconds=$((TIMEOUT_MINUTES * 60))
local temp_state_file=$(mktemp)
while IFS=':' read -r user ip last_seen violation_time; do
if [[ -z "$user" || -z "$ip" || -z "$last_seen" ]]; then continue; fi
local elapsed=$((current_time - last_seen))
if [ "$elapsed" -lt "$timeout_seconds" ]; then
if [[ -n "$violation_time" ]]; then
local violation_elapsed=$((current_time - violation_time))
if [ "$violation_elapsed" -ge "$GRACE_PERIOD_SECONDS" ]; then
echo "[$(date)] >>> EKSEKUSI: Timer habis. Memutus & memblokir IP '$ip' (user: '$user')..."
sudo conntrack -D -s "$ip" -p udp 2>/dev/null
if ! sudo iptables -C "$IPTABLES_CHAIN" -s "$ip" -j DROP &> /dev/null; then
sudo iptables -A "$IPTABLES_CHAIN" -s "$ip" -j DROP -m comment --comment "Kicked $user on $(date +%F-%T)"
fi
queue_flush_bandwidth "$user" "$ip"
continue
else
echo "${user}:${ip}:${last_seen}:${violation_time}" >> "$temp_state_file"
fi
else
echo "${user}:${ip}:${last_seen}:" >> "$temp_state_file"
fi
else
echo "[$(date)] Sesi untuk user '$user' dari IP '$ip' kadaluarsa. Membuka blokir..."
local rule_num=$(sudo iptables -L "$IPTABLES_CHAIN" --line-numbers -n | grep -w "$ip" | awk '{print $1}' | head -n 1)
if [ -n "$rule_num" ]; then
sudo iptables -D "$IPTABLES_CHAIN" "$rule_num"
fi
queue_flush_bandwidth "$user" "$ip"
fi
done < "$STATE_FILE"
mv "$temp_state_file" "$STATE_FILE"
echo "[$(date)] =====> PEMBERSIHAN SELESAI <====="
}
# =================================================================
# --- FUNGSI BLOCKING SEGERA ---
# =================================================================
function immediate_block() {
local user="$1"
local old_ip="$2"
echo "[$(date)] >>> EKSEKUSI SEGERA: Memutus & memblokir IP '$old_ip' (user: '$user')..."
sudo conntrack -D -s "$old_ip" -p udp 2>/dev/null
if ! sudo iptables -C "$IPTABLES_CHAIN" -s "$old_ip" -j DROP &> /dev/null; then
sudo iptables -A "$IPTABLES_CHAIN" -s "$old_ip" -j DROP -m comment --comment "Kicked $user on $(date +%F-%T)"
fi
queue_flush_bandwidth "$user" "$old_ip"
}
# =================================================================
# --- FUNGSI BANDWIDTH ---
# =================================================================
function add_bandwidth_counter() {
local ip="$1"
local user="$2"
if ! sudo iptables -C "$BANDWIDTH_CHAIN" -s "$ip" -p udp -j RETURN -m comment --comment "user:$user" &> /dev/null; then
sudo iptables -A "$BANDWIDTH_CHAIN" -s "$ip" -p udp -j RETURN -m comment --comment "user:$user"
fi
}
function queue_flush_bandwidth() {
local user="$1"
local ip="$2"
echo "$user $ip" >> "$FLUSH_QUEUE"
}
### [NEW] Fungsi khusus menabung ke Buku Harian ###
function update_daily_ledger() {
local user="$1"
local bytes="$2"
if [[ -z "$user" || -z "$bytes" || ! "$bytes" =~ ^[0-9]+$ ]]; then return; fi
if [ -f "$DAILY_LEDGER" ] && grep -q "^${user}:" "$DAILY_LEDGER"; then
awk -F':' -v u="$user" -v b="$bytes" '$1 == u {print $1":"($2+b)} $1 != u' "$DAILY_LEDGER" > "${DAILY_LEDGER}.tmp"
mv "${DAILY_LEDGER}.tmp" "$DAILY_LEDGER"
else
echo "${user}:${bytes}" >> "$DAILY_LEDGER"
fi
}
function monitor_bandwidth() {
local current_time=$(date +%s)
local today_date=$(date +%F)
local time_5m_ago=$((current_time - 300))
local time_1h_ago=$((current_time - 3600))
local time_limit_history=$((current_time - 3900))
# ==========================================================
# LOGIKA TUTUP BUKU HARIAN (MENYIMPAN BUKU HARIAN KE ARSIP)
# ==========================================================
local last_day=$(cat "$DAILY_STATE" 2>/dev/null)
if [[ -n "$last_day" && "$today_date" != "$last_day" ]]; then
echo "[$(date)] >>> TUTUP BUKU HARIAN: Mengarsipkan seluruh data $last_day..."
if [ -s "$DAILY_LEDGER" ]; then
mv "$DAILY_LEDGER" "$DAILY_LOG_DIR/${last_day}.log"
else
touch "$DAILY_LOG_DIR/${last_day}.log"
fi
> "$ACCUMULATOR_FILE"
> "$HISTORY_1H_FILE"
> "$DAILY_LEDGER" # Reset Buku Harian untuk hari baru
echo "$today_date" > "$DAILY_STATE"
elif [[ -z "$last_day" ]]; then
echo "$today_date" > "$DAILY_STATE"
fi
# ==========================================================
local temp_iptables=$(mktemp)
local temp_active_users=$(mktemp)
sudo iptables -L "$BANDWIDTH_CHAIN" -v -n -x 2>/dev/null > "$temp_iptables"
grep "user:" "$temp_iptables" | grep -oP 'user:\K[^ ]+' | sort -u > "$temp_active_users"
# PROSES ANTRIAN (User yang putus/tendang)
if [ -s "$FLUSH_QUEUE" ]; then
while read -r user ip; do
if [[ -z "$user" || -z "$ip" ]]; then continue; fi
echo "$user" >> "$temp_active_users"
local bytes=$(grep -w "$ip" "$temp_iptables" | awk '{print $2}')
if [[ -n "$bytes" && "$bytes" =~ ^[0-9]+$ && "$bytes" -gt 0 ]]; then
if grep -q "^${user}:" "$ACCUMULATOR_FILE"; then
awk -F':' -v u="$user" -v b="$bytes" -v t="$current_time" '$1 == u {print $1":"($2+b)":"t} $1 != u' "$ACCUMULATOR_FILE" > "${ACCUMULATOR_FILE}.tmp"
mv "${ACCUMULATOR_FILE}.tmp" "$ACCUMULATOR_FILE"
else
echo "${user}:${bytes}:${current_time}" >> "$ACCUMULATOR_FILE"
fi
update_daily_ledger "$user" "$bytes" ### [NEW] ###
else
awk -F':' -v u="$user" -v t="$current_time" '$1 == u {print $1":"$2":"t} $1 != u' "$ACCUMULATOR_FILE" > "${ACCUMULATOR_FILE}.tmp"
mv "${ACCUMULATOR_FILE}.tmp" "$ACCUMULATOR_FILE"
fi
local rule_num=$(sudo iptables -L "$BANDWIDTH_CHAIN" --line-numbers -n | grep -w "$ip" | awk '{print $1}' | head -n 1)
if [ -n "$rule_num" ]; then
sudo iptables -D "$BANDWIDTH_CHAIN" "$rule_num" 2>/dev/null
fi
done < "$FLUSH_QUEUE"
> "$FLUSH_QUEUE"
fi
# PROSES USER YANG SEDANG ONLINE
while read -r line; do
local user=$(echo "$line" | grep -oP 'user:\K[^ ]+')
local bytes=$(echo "$line" | awk '{print $2}')
if [[ -n "$user" && -n "$bytes" && "$bytes" =~ ^[0-9]+$ ]]; then
if grep -q "^${user}:" "$ACCUMULATOR_FILE"; then
awk -F':' -v u="$user" -v b="$bytes" -v t="$current_time" '$1 == u {print $1":"($2+b)":"t} $1 != u' "$ACCUMULATOR_FILE" > "${ACCUMULATOR_FILE}.tmp"
mv "${ACCUMULATOR_FILE}.tmp" "$ACCUMULATOR_FILE"
else
echo "${user}:${bytes}:${current_time}" >> "$ACCUMULATOR_FILE"
fi
update_daily_ledger "$user" "$bytes" ### [NEW] ###
fi
done < <(grep "user:" "$temp_iptables")
# RESET COUNTER IPTABLES KE NOL
sudo iptables -Z "$BANDWIDTH_CHAIN" &>/dev/null
# SIMPAN SNAPSHOT UNTUK 1 JAM
local temp_history=$(mktemp)
while IFS=':' read -r user bytes last_active; do
if [[ -n "$user" && -n "$bytes" ]]; then
echo "${current_time}:${user}:${bytes}" >> "$temp_history"
fi
done < "$ACCUMULATOR_FILE"
if [ -f "$HISTORY_1H_FILE" ]; then
cat "$HISTORY_1H_FILE" "$temp_history" | awk -v limit="$time_limit_history" '$1 >= limit' > "${temp_history}.merged"
mv "${temp_history}.merged" "$HISTORY_1H_FILE"
else
mv "$temp_history" "$HISTORY_1H_FILE"
fi
# HAPUS USER DARI ACCUMULATOR JIKA SUDAH 1 JAM OFFLINE (Agar Menu 7 tetap bersih)
local temp_acc_clean=$(mktemp)
sort -u "$temp_active_users" > "${temp_active_users}.uniq"
while IFS=':' read -r user bytes last_active; do
if [[ -z "$user" ]]; then continue; fi
if grep -qx "$user" "${temp_active_users}.uniq" || [ -n "$last_active" ] && [ "$last_active" -gt "$time_1h_ago" ]; then
echo "${user}:${bytes}:${last_active}" >> "$temp_acc_clean"
fi
done < "$ACCUMULATOR_FILE"
mv "$temp_acc_clean" "$ACCUMULATOR_FILE"
# RENDER LAPORAN 5 MENIT & 1 JAM
{
echo "============================================================"
echo " LAPORAN BANDWIDTH AKUMULASI USER - $(date '+%Y-%m-%d %H:%M:%S')"
echo "============================================================"
printf "%-15s %14s %14s %12s\n" "USER" "5_MIN(MB)" "1_HOUR(MB)" "AVG(Mbps)"
echo "------------------------------------------------------------"
while IFS=':' read -r user bytes_now last_active; do
if [[ -z "$user" || -z "$bytes_now" ]]; then continue; fi
local bytes_5m_ago=$(grep ".*:${user}:" "$HISTORY_1H_FILE" | awk -F':' -v limit="$time_5m_ago" '$1 <= limit {print $3}' | tail -n 1)
if [ -z "$bytes_5m_ago" ] || ! [[ "$bytes_5m_ago" =~ ^[0-9]+$ ]]; then bytes_5m_ago=0; fi
local bytes_1h_ago=$(grep ".*:${user}:" "$HISTORY_1H_FILE" | awk -F':' -v limit="$time_1h_ago" '$1 <= limit {print $3}' | tail -n 1)
if [ -z "$bytes_1h_ago" ] || ! [[ "$bytes_1h_ago" =~ ^[0-9]+$ ]]; then bytes_1h_ago=0; fi
local diff_5m=$((bytes_now - bytes_5m_ago))
if [ "$diff_5m" -lt 0 ]; then diff_5m=0; fi
local diff_1h=$((bytes_now - bytes_1h_ago))
if [ "$diff_1h" -lt 0 ]; then diff_1h=0; fi
local mb_5m=$(echo "scale=2; $diff_5m / 1048576" | bc 2>/dev/null || echo "0.00")
local mb_1h=$(echo "scale=2; $diff_1h / 1048576" | bc 2>/dev/null || echo "0.00")
local mbps=$(echo "scale=2; ($diff_5m * 8) / 3000000" | bc 2>/dev/null || echo "0.00")
printf "%-15s %14s %14s %12s\n" "$user" "$mb_5m" "$mb_1h" "$mbps"
done < "$ACCUMULATOR_FILE"
echo "============================================================"
echo "Catatan: User tetap tampil 1 jam setelah terakhir terhubung"
echo "============================================================"
} | tee "$BANDWIDTH_LOG"
rm -f "$temp_iptables" "$temp_active_users" "${temp_active_users}.uniq"
}
# =================================================================
# --- INISIALISASI PENJAGA ---
# =================================================================
if ! sudo iptables -L "$IPTABLES_CHAIN" &> /dev/null; then
sudo iptables -N "$IPTABLES_CHAIN"
fi
if ! sudo iptables -L "$BANDWIDTH_CHAIN" &> /dev/null; then
sudo iptables -N "$BANDWIDTH_CHAIN"
else
sudo iptables -L "$BANDWIDTH_CHAIN" -v -n -x 2>/dev/null | grep "user:" | while read -r line; do
user=$(echo "$line" | grep -oP 'user:\K[^ ]+')
ip=$(echo "$line" | awk '{print $8}')
if [[ -n "$user" && -n "$ip" ]]; then
queue_flush_bandwidth "$user" "$ip"
fi
done
fi
echo "[$(date)] Memastikan aturan iptables berada di posisi teratas..."
sudo iptables -D INPUT -p udp --dport "$PROTECTED_PORT" -j "$IPTABLES_CHAIN" 2>/dev/null
sudo iptables -D FORWARD -p udp --dport "$PROTECTED_PORT" -j "$IPTABLES_CHAIN" 2>/dev/null
sudo iptables -D INPUT -p udp --dport "$PROTECTED_PORT" -j "$BANDWIDTH_CHAIN" 2>/dev/null
sudo iptables -F "$IPTABLES_CHAIN"
sudo iptables -I INPUT 1 -p udp --dport "$PROTECTED_PORT" -j "$BANDWIDTH_CHAIN"
sudo iptables -I INPUT 2 -p udp --dport "$PROTECTED_PORT" -j "$IPTABLES_CHAIN"
sudo iptables -I FORWARD 1 -p udp --dport "$PROTECTED_PORT" -j "$IPTABLES_CHAIN"
while true; do cleanup; sleep 60; done &
CLEANUP_PID=$!
trap 'kill $CLEANUP_PID; exit' SIGTERM SIGINT
while true; do monitor_bandwidth; sleep 300; done &
BANDWIDTH_PID=$!
trap 'kill $BANDWIDTH_PID; exit' SIGTERM SIGINT
echo "[$(date)] Memuat memori awal dari $MEMORY_MINUTES menit terakhir..."
sudo journalctl -u "$UDP_SERVICE_NAME" --since "$MEMORY_MINUTES minutes ago" --no-pager | grep "Client connected" | sort | awk -F'[][]' '
{
for (i=1; i<=NF; i++) {
if ($i ~ /src:/) { split($i, s, ":"); ip = s[2]; }
if ($i ~ /user:/) { split($i, u, ":"); user = u[2]; }
}
if (user && ip) print user ":" ip ":" systime() ":" > "'"$STATE_FILE"'"
}'
awk -i inplace '!seen[$0]++' "$STATE_FILE"
while IFS=':' read -r user ip last_seen violation_time; do
if [[ -n "$user" && -n "$ip" ]]; then
add_bandwidth_counter "$ip" "$user"
fi
done < "$STATE_FILE"
echo "[$(date)] Memori awal selesai. Penjaga siap bertugas!"
echo "[$(date)] Memantau log layanan: $UDP_SERVICE_NAME"
sudo journalctl -u "$UDP_SERVICE_NAME" -f --no-tail | while read line; do
if [[ "$line" == *"[INFO]"*"[src:"*"[user:"*"Client connected"* ]]; then
IP=$(echo "$line" | awk -F'[][]' '{for(i=1;i<=NF;i++){if($i ~ /src:/){split($i, a, ":"); print a[2]}}}')
USER=$(echo "$line" | awk -F'[][]' '{for(i=1;i<=NF;i++){if($i ~ /user:/){split($i, a, ":"); print a[2]}}}')
if [[ -n "$IP" && -n "$USER" ]]; then
echo "[$(date)] Koneksi baru: User '$USER' dari IP '$IP'"
add_bandwidth_counter "$IP" "$USER"
LAST_ENTRY=$(grep "^${USER}:" "$STATE_FILE" | tail -n 1)
LAST_IP=$(echo "$LAST_ENTRY" | cut -d':' -f2)
if [[ -n "$LAST_IP" && "$LAST_IP" != "$IP" ]]; then
echo "[$(date)] >>> PELANGGARAN: User '$USER' multi-login (IP lama: '$LAST_IP', IP baru: '$IP')."
immediate_block "$USER" "$LAST_IP"
sed -i "/^${USER}:${LAST_IP}:/d" "$STATE_FILE"
fi
echo "${USER}:${IP}:$(date +%s)::" >> "$STATE_FILE"
awk -i inplace '!seen[$0]++' "$STATE_FILE"
fi
elif [[ "$line" == *"[INFO]"*"[src:"*"[user:"*"Client disconnected"* ]]; then
IP=$(echo "$line" | awk -F'[][]' '{for(i=1;i<=NF;i++){if($i ~ /src:/){split($i, a, ":"); print a[2]}}}')
USER=$(echo "$line" | awk -F'[][]' '{for(i=1;i<=NF;i++){if($i ~ /user:/){split($i, a, ":"); print a[2]}}}')
if [[ -n "$IP" && -n "$USER" ]]; then
echo "[$(date)] User '$USER' dari IP '$IP' disconnect. Memasukkan antrian riwayat..."
queue_flush_bandwidth "$USER" "$IP"
sed -i "/^${USER}:${IP}:/d" "$STATE_FILE"
fi
fi
done
4.Berikan izin eksekusi:
sudo chmod +x /opt/udp-proxy/bin/enforcer_service.sh
Langkah 3: Membuat Layanan systemd
1.Buat file layanan:
sudo nano /etc/systemd/system/udp-enforcer.service
2.Salin dan tempel konfigurasi berikut:
[Unit]
Description=UDP Connection Enforcer Service
After=network.target udp-custom.service
Wants=udp-custom.service
[Service]
Type=simple
ExecStart=/opt/udp-proxy/bin/enforcer_service.sh
Restart=always
RestartSec=10
User=root
[Install]
WantedBy=multi-user.target
3.Simpan dan keluar (Ctrl+X, Y, Enter).
Langkah 4: Membuat Perintah Kontrol Manual (enforcer-on / enforcer-off)
Ini adalah kunci untuk kontrol yang "ber-ingat".
1.Buat perintah enforcer-on:
sudo nano /usr/local/bin/enforcer-on
Salin dan tempel:
#!/bin/bash
FLAG_FILE="/opt/udp-proxy/log/enforcer.enabled"
SERVICE_NAME="udp-enforcer.service"
echo "Mengaktifkan Penjaga Koneksi..."
# Buat direktori log jika belum ada untuk mencegah error
sudo mkdir -p "$(dirname "$FLAG_FILE")"
sudo touch "$FLAG_FILE"
sudo systemctl start "$SERVICE_NAME"
sudo systemctl enable "$SERVICE_NAME"
echo "Penjaga telah diaktifkan. Statusnya akan diingat setelah reboot."
2.Buat perintah enforcer-off:
sudo nano /usr/local/bin/enforcer-off
Salin dan tempel:
#!/bin/bash
FLAG_FILE="/opt/udp-proxy/log/enforcer.enabled"
SERVICE_NAME="udp-enforcer.service"
echo "Menonaktifkan Penjaga Koneksi..."
sudo systemctl stop "$SERVICE_NAME"
sudo rm -f "$FLAG_FILE"
echo "Penjaga telah dinonaktifkan. Statusnya akan diingat setelah reboot."
3.Berikan izin eksekusi pada kedua perintah:
sudo chmod +x /usr/local/bin/enforcer-on
sudo chmod +x /usr/local/bin/enforcer-off
5.Langkah 5: Finalisasi dan Aktivasi Awal
Langkah terakhir untuk menyelesaikan setup dan mengaktifkan Penjaga untuk pertama kalinya.
1.Muat ulang systemd untuk mengenali layanan baru:
sudo systemctl daemon-reload
2.Aktifkan layanan sekali saja
sudo systemctl enable udp-enforcer.service
3.Aktifkan Penjaga untuk pertama kalinya:
enforcer-on
4.Verifikasi statusnya:
sudo systemctl status udp-enforcer.service
Anda harus melihat Active: active (running).
Jalankan perintah-perintah ini secara manual untuk pengecekan urutan aturan:
sudo iptables -D INPUT -p udp --dport 36712 -j UDP_ENFORCER
sudo iptables -I INPUT 1 -p udp --dport 36712 -j UDP_ENFORCER
Sekarang, coba lagi perintah :
sudo iptables -L INPUT -n -v --line-numbers
Hasilnya harusnya terlihat seperti ini:
Chain INPUT (policy ACCEPT ...)
num pkts bytes target prot opt in out source destination
1 0 0 UDP_ENFORCER udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:36712
2 1355K 318M ACCEPT udp -- * * 0.0.0.0/0 0.0.0.0/0 udp dpt:36712
...
Sekarang UDP_ENFORCER ada di posisi #1. Sistem blokir seharusnya sudah bekerja!
Restart layanan untuk menerapkan perubahan:
sudo systemctl restart udp-enforcer.service
Ringkasan Perintah Tanpa Panel/Manual
Mengaktifkan Penjaga
enforcer-on
Menonaktifkan Penjaga
enforcer-off
Cek Status Layanan
sudo systemctl status udp-enforcer.service
Lihat Log Aktivitas
sudo journalctl -u udp-enforcer.service -f --no-pager
Lihat IP yang Diblokir
sudo iptables -L UDP_ENFORCER -n -v
Lepas Semua Blokir
sudo iptables -F UDP_ENFORCER
Hapus File State (Reset)
echo "" > /opt/udp-proxy/log/enforcer_state.log




