menu.sh 140 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337
  1. #!/bin/bash
  2. C_RESET=$'\033[0m'
  3. C_BOLD=$'\033[1m'
  4. C_DIM=$'\033[2m'
  5. C_UL=$'\033[4m'
  6. # Premium Color Palette
  7. C_RED=$'\033[38;5;196m' # Bright Red
  8. C_GREEN=$'\033[38;5;46m' # Neon Green
  9. C_YELLOW=$'\033[38;5;226m' # Bright Yellow
  10. C_BLUE=$'\033[38;5;39m' # Deep Sky Blue
  11. C_PURPLE=$'\033[38;5;135m' # Light Purple
  12. C_CYAN=$'\033[38;5;51m' # Cyan
  13. C_WHITE=$'\033[38;5;255m' # Bright White
  14. C_GRAY=$'\033[38;5;245m' # Gray
  15. C_ORANGE=$'\033[38;5;208m' # Orange
  16. # Semantic Aliases
  17. C_TITLE=$C_PURPLE
  18. C_CHOICE=$C_CYAN
  19. C_PROMPT=$C_BLUE
  20. C_WARN=$C_YELLOW
  21. C_DANGER=$C_RED
  22. C_STATUS_A=$C_GREEN
  23. C_STATUS_I=$C_GRAY
  24. C_ACCENT=$C_ORANGE
  25. DB_DIR="/etc/firewallfalcon"
  26. DB_FILE="$DB_DIR/users.db"
  27. INSTALL_FLAG_FILE="$DB_DIR/.install"
  28. BADVPN_SERVICE_FILE="/etc/systemd/system/badvpn.service"
  29. BADVPN_BUILD_DIR="/root/badvpn-build"
  30. HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
  31. NGINX_CONFIG_FILE="/etc/nginx/sites-available/default"
  32. SSL_CERT_DIR="/etc/firewallfalcon/ssl"
  33. SSL_CERT_FILE="$SSL_CERT_DIR/firewallfalcon.pem"
  34. NGINX_PORTS_FILE="$DB_DIR/nginx_ports.conf"
  35. DNSTT_SERVICE_FILE="/etc/systemd/system/dnstt.service"
  36. DNSTT_BINARY="/usr/local/bin/dnstt-server"
  37. DNSTT_KEYS_DIR="/etc/firewallfalcon/dnstt"
  38. DNSTT_CONFIG_FILE="$DB_DIR/dnstt_info.conf"
  39. DNS_INFO_FILE="$DB_DIR/dns_info.conf"
  40. UDP_CUSTOM_DIR="/root/udp"
  41. UDP_CUSTOM_SERVICE_FILE="/etc/systemd/system/udp-custom.service"
  42. SSH_BANNER_FILE="/etc/bannerssh"
  43. FALCONPROXY_SERVICE_FILE="/etc/systemd/system/falconproxy.service"
  44. FALCONPROXY_BINARY="/usr/local/bin/falconproxy"
  45. FALCONPROXY_CONFIG_FILE="$DB_DIR/falconproxy_config.conf"
  46. LIMITER_SCRIPT="/usr/local/bin/firewallfalcon-limiter.sh"
  47. LIMITER_SERVICE="/etc/systemd/system/firewallfalcon-limiter.service"
  48. BANDWIDTH_DIR="$DB_DIR/bandwidth"
  49. BANDWIDTH_SCRIPT="/usr/local/bin/firewallfalcon-bandwidth.sh"
  50. BANDWIDTH_SERVICE="/etc/systemd/system/firewallfalcon-bandwidth.service"
  51. TRIAL_CLEANUP_SCRIPT="/usr/local/bin/firewallfalcon-trial-cleanup.sh"
  52. LOGIN_INFO_SCRIPT="/usr/local/bin/firewallfalcon-login-info.sh"
  53. SSHD_FF_CONFIG="/etc/ssh/sshd_config.d/firewallfalcon.conf"
  54. # --- ZiVPN Variables ---
  55. ZIVPN_DIR="/etc/zivpn"
  56. ZIVPN_BIN="/usr/local/bin/zivpn"
  57. ZIVPN_SERVICE_FILE="/etc/systemd/system/zivpn.service"
  58. ZIVPN_CONFIG_FILE="$ZIVPN_DIR/config.json"
  59. ZIVPN_CERT_FILE="$ZIVPN_DIR/zivpn.crt"
  60. ZIVPN_KEY_FILE="$ZIVPN_DIR/zivpn.key"
  61. DESEC_TOKEN="V55cFY8zTictLCPfviiuX5DHjs15"
  62. DESEC_DOMAIN="manager.firewallfalcon.qzz.io"
  63. SELECTED_USER=""
  64. UNINSTALL_MODE="interactive"
  65. if [[ $EUID -ne 0 ]]; then
  66. echo -e "${C_RED}❌ Error: This script requires root privileges to run.${C_RESET}"
  67. exit 1
  68. fi
  69. # Mandatory Dependency Check (Added jq and curl)
  70. check_environment() {
  71. # Mandatory Dependency Check (Added jq and curl)
  72. for cmd in bc jq curl wget; do
  73. if ! command -v $cmd &> /dev/null; then
  74. echo -e "${C_YELLOW}⚠️ Warning: '$cmd' not found. Installing...${C_RESET}"
  75. apt-get update > /dev/null 2>&1 && apt-get install -y $cmd || {
  76. echo -e "${C_RED}❌ Error: Failed to install '$cmd'. Please install it manually.${C_RESET}"
  77. exit 1
  78. }
  79. fi
  80. done
  81. }
  82. initial_setup() {
  83. echo -e "${C_BLUE}⚙️ Initializing FirewallFalcon Manager setup...${C_RESET}"
  84. check_environment
  85. mkdir -p "$DB_DIR"
  86. touch "$DB_FILE"
  87. mkdir -p "$SSL_CERT_DIR"
  88. mkdir -p "$BANDWIDTH_DIR"
  89. echo -e "${C_BLUE}🔹 Configuring user limiter service...${C_RESET}"
  90. setup_limiter_service
  91. echo -e "${C_BLUE}🔹 Configuring bandwidth monitoring service...${C_RESET}"
  92. setup_bandwidth_service
  93. echo -e "${C_BLUE}🔹 Installing trial account cleanup script...${C_RESET}"
  94. setup_trial_cleanup_script
  95. echo -e "${C_BLUE}🔹 Configuring SSH login info banner...${C_RESET}"
  96. setup_ssh_login_info
  97. if [ ! -f "$INSTALL_FLAG_FILE" ]; then
  98. touch "$INSTALL_FLAG_FILE"
  99. fi
  100. echo -e "${C_GREEN}✅ Setup finished.${C_RESET}"
  101. }
  102. _is_valid_ipv4() {
  103. local ip=$1
  104. if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
  105. return 0
  106. else
  107. return 1
  108. fi
  109. }
  110. check_and_open_firewall_port() {
  111. local port="$1"
  112. local protocol="${2:-tcp}"
  113. local firewall_detected=false
  114. if command -v ufw &> /dev/null && ufw status | grep -q "Status: active"; then
  115. firewall_detected=true
  116. if ! ufw status | grep -qw "$port/$protocol"; then
  117. echo -e "${C_YELLOW}🔥 UFW firewall is active and port ${port}/${protocol} is closed.${C_RESET}"
  118. read -p "👉 Do you want to open this port now? (y/n): " confirm
  119. if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
  120. ufw allow "$port/$protocol"
  121. echo -e "${C_GREEN}✅ Port ${port}/${protocol} has been opened in UFW.${C_RESET}"
  122. else
  123. echo -e "${C_RED}❌ Warning: Port ${port}/${protocol} was not opened. The service may not work correctly.${C_RESET}"
  124. return 1
  125. fi
  126. else
  127. echo -e "${C_GREEN}✅ Port ${port}/${protocol} is already open in UFW.${C_RESET}"
  128. fi
  129. fi
  130. if command -v firewall-cmd &> /dev/null && systemctl is-active --quiet firewalld; then
  131. firewall_detected=true
  132. if ! firewall-cmd --list-ports --permanent | grep -qw "$port/$protocol"; then
  133. echo -e "${C_YELLOW}🔥 firewalld is active and port ${port}/${protocol} is not open.${C_RESET}"
  134. read -p "👉 Do you want to open this port now? (y/n): " confirm
  135. if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
  136. firewall-cmd --add-port="$port/$protocol" --permanent
  137. firewall-cmd --reload
  138. echo -e "${C_GREEN}✅ Port ${port}/${protocol} has been opened in firewalld.${C_RESET}"
  139. else
  140. echo -e "${C_RED}❌ Warning: Port ${port}/${protocol} was not opened. The service may not work correctly.${C_RESET}"
  141. return 1
  142. fi
  143. else
  144. echo -e "${C_GREEN}✅ Port ${port}/${protocol} is already open in firewalld.${C_RESET}"
  145. fi
  146. fi
  147. if ! $firewall_detected; then
  148. echo -e "${C_BLUE}ℹ️ No active firewall (UFW or firewalld) detected. Assuming ports are open.${C_RESET}"
  149. fi
  150. return 0
  151. }
  152. check_and_free_ports() {
  153. local ports_to_check=("$@")
  154. for port in "${ports_to_check[@]}"; do
  155. echo -e "\n${C_BLUE}🔎 Checking if port $port is available...${C_RESET}"
  156. local conflicting_process_info
  157. conflicting_process_info=$(ss -lntp | grep ":$port\s" || ss -lunp | grep ":$port\s")
  158. if [[ -n "$conflicting_process_info" ]]; then
  159. local conflicting_pid
  160. conflicting_pid=$(echo "$conflicting_process_info" | grep -oP 'pid=\K[0-9]+' | head -n 1)
  161. local conflicting_name
  162. conflicting_name=$(echo "$conflicting_process_info" | grep -oP 'users:\(\("(\K[^"]+)' | head -n 1)
  163. echo -e "${C_YELLOW}⚠️ Warning: Port $port is in use by process '${conflicting_name:-unknown}' (PID: ${conflicting_pid:-N/A}).${C_RESET}"
  164. read -p "👉 Do you want to attempt to stop this process? (y/n): " kill_confirm
  165. if [[ "$kill_confirm" == "y" || "$kill_confirm" == "Y" ]]; then
  166. echo -e "${C_GREEN}🛑 Stopping process PID $conflicting_pid...${C_RESET}"
  167. systemctl stop "$(ps -p "$conflicting_pid" -o comm=)" &>/dev/null || kill -9 "$conflicting_pid"
  168. sleep 2
  169. if ss -lntp | grep -q ":$port\s" || ss -lunp | grep -q ":$port\s"; then
  170. echo -e "${C_RED}❌ Failed to free port $port. Please handle it manually. Aborting.${C_RESET}"
  171. return 1
  172. else
  173. echo -e "${C_GREEN}✅ Port $port has been successfully freed.${C_RESET}"
  174. fi
  175. else
  176. echo -e "${C_RED}❌ Cannot proceed without freeing port $port. Aborting.${C_RESET}"
  177. return 1
  178. fi
  179. else
  180. echo -e "${C_GREEN}✅ Port $port is free to use.${C_RESET}"
  181. fi
  182. done
  183. return 0
  184. }
  185. setup_limiter_service() {
  186. # Combined limiter + bandwidth monitoring
  187. cat > "$LIMITER_SCRIPT" << 'EOF'
  188. #!/bin/bash
  189. DB_FILE="/etc/firewallfalcon/users.db"
  190. BW_DIR="/etc/firewallfalcon/bandwidth"
  191. PID_DIR="$BW_DIR/pidtrack"
  192. mkdir -p "$BW_DIR" "$PID_DIR"
  193. while true; do
  194. if [[ ! -f "$DB_FILE" ]]; then
  195. sleep 30
  196. continue
  197. fi
  198. current_ts=$(date +%s)
  199. while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
  200. [[ -z "$user" || "$user" == \#* ]] && continue
  201. # --- Expiry Check ---
  202. if [[ "$expiry" != "Never" && "$expiry" != "" ]]; then
  203. expiry_ts=$(date -d "$expiry" +%s 2>/dev/null || echo 0)
  204. if [[ $expiry_ts -lt $current_ts && $expiry_ts -ne 0 ]]; then
  205. if ! passwd -S "$user" | grep -q " L "; then
  206. usermod -L "$user" &>/dev/null
  207. killall -u "$user" -9 &>/dev/null
  208. fi
  209. continue
  210. fi
  211. fi
  212. # --- Connection Limit Check ---
  213. online_count=$(pgrep -c -u "$user" sshd)
  214. if ! [[ "$limit" =~ ^[0-9]+$ ]]; then limit=1; fi
  215. if [[ "$online_count" -gt "$limit" ]]; then
  216. if ! passwd -S "$user" | grep -q " L "; then
  217. usermod -L "$user" &>/dev/null
  218. killall -u "$user" -9 &>/dev/null
  219. (sleep 120; usermod -U "$user" &>/dev/null) &
  220. else
  221. killall -u "$user" -9 &>/dev/null
  222. fi
  223. fi
  224. # --- SSH Banner Generation (Delay of 1 cycle for BW stats is fine) ---
  225. if [[ -f "/etc/firewallfalcon/banners_enabled" ]]; then
  226. mkdir -p "/etc/firewallfalcon/banners"
  227. days_left="N/A"
  228. if [[ "$expiry" != "Never" && -n "$expiry" ]]; then
  229. if [[ $expiry_ts -gt 0 ]]; then
  230. diff_secs=$((expiry_ts - current_ts))
  231. if [[ $diff_secs -le 0 ]]; then
  232. days_left="EXPIRED"
  233. else
  234. d_l=$(( diff_secs / 86400 ))
  235. h_l=$(( (diff_secs % 86400) / 3600 ))
  236. if [[ $d_l -eq 0 ]]; then days_left="${h_l}h left"
  237. else days_left="${d_l}d ${h_l}h"; fi
  238. fi
  239. fi
  240. fi
  241. bw_info="Unlimited"
  242. if [[ "$bandwidth_gb" != "0" && -n "$bandwidth_gb" ]]; then
  243. usagefile="$BW_DIR/${user}.usage"
  244. accum_disp=0
  245. [[ -f "$usagefile" ]] && accum_disp=$(cat "$usagefile" 2>/dev/null)
  246. used_gb=$(awk "BEGIN {printf \"%.2f\", $accum_disp / 1073741824}")
  247. remain_gb=$(awk "BEGIN {r=$bandwidth_gb - $used_gb; if(r<0) r=0; printf \"%.2f\", r}")
  248. bw_info="${used_gb}/${bandwidth_gb} GB used | ${remain_gb} GB left"
  249. fi
  250. # Format the output with HTML tags since clients like HTTP Custom render Server Messages using Html.fromHtml()
  251. # Crucial: Use echo -e instead of heredoc to prevent DOS CRLF syntax errors when moving script to Linux
  252. echo -e "<br><font color=\"yellow\"><b> ✨ ACCOUNT STATUS ✨ </b></font><br><br>" > "/etc/firewallfalcon/banners/${user}.txt"
  253. echo -e "<font color=\"white\">👤 <b>Username :</b> $user</font><br>" >> "/etc/firewallfalcon/banners/${user}.txt"
  254. echo -e "<font color=\"white\">📅 <b>Expiration :</b> $expiry ($days_left)</font><br>" >> "/etc/firewallfalcon/banners/${user}.txt"
  255. echo -e "<font color=\"white\">📊 <b>Bandwidth :</b> $bw_info</font><br>" >> "/etc/firewallfalcon/banners/${user}.txt"
  256. echo -e "<font color=\"white\">🔌 <b>Sessions :</b> $online_count/$limit</font><br><br>" >> "/etc/firewallfalcon/banners/${user}.txt"
  257. fi
  258. # --- Bandwidth Check ---
  259. [[ -z "$bandwidth_gb" || "$bandwidth_gb" == "0" ]] && continue
  260. # Get user UID
  261. user_uid=$(id -u "$user" 2>/dev/null)
  262. [[ -z "$user_uid" ]] && continue
  263. # Find sshd PIDs for this user via loginuid
  264. pids=""
  265. # Method 1: pgrep
  266. m1=$(pgrep -u "$user" sshd 2>/dev/null | tr '\n' ' ')
  267. pids="$m1"
  268. # Method 2: loginuid scan
  269. for p in /proc/[0-9]*/loginuid; do
  270. [[ ! -f "$p" ]] && continue
  271. luid=$(cat "$p" 2>/dev/null)
  272. [[ -z "$luid" || "$luid" == "4294967295" ]] && continue
  273. [[ "$luid" != "$user_uid" ]] && continue
  274. pid_dir=$(dirname "$p")
  275. pid_num=$(basename "$pid_dir")
  276. cname=$(cat "$pid_dir/comm" 2>/dev/null)
  277. [[ "$cname" != "sshd" ]] && continue
  278. ppid_val=$(awk '/^PPid:/{print $2}' "$pid_dir/status" 2>/dev/null)
  279. [[ "$ppid_val" == "1" ]] && continue
  280. pids="$pids $pid_num"
  281. done
  282. # Deduplicate
  283. pids=$(echo "$pids" | tr ' ' '\n' | sort -u | grep -v '^$' | tr '\n' ' ')
  284. # Read accumulated usage
  285. usagefile="$BW_DIR/${user}.usage"
  286. accumulated=0
  287. if [[ -f "$usagefile" ]]; then
  288. accumulated=$(cat "$usagefile" 2>/dev/null)
  289. if ! [[ "$accumulated" =~ ^[0-9]+$ ]]; then accumulated=0; fi
  290. fi
  291. if [[ -z "$pids" ]]; then
  292. rm -f "$PID_DIR/${user}__"*.last 2>/dev/null
  293. continue
  294. fi
  295. delta_total=0
  296. for pid in $pids; do
  297. [[ -z "$pid" ]] && continue
  298. io_file="/proc/$pid/io"
  299. if [[ -r "$io_file" ]]; then
  300. rchar=$(awk '/^rchar:/{print $2}' "$io_file" 2>/dev/null)
  301. wchar=$(awk '/^wchar:/{print $2}' "$io_file" 2>/dev/null)
  302. [[ -z "$rchar" ]] && rchar=0
  303. [[ -z "$wchar" ]] && wchar=0
  304. cur=$((rchar + wchar))
  305. else
  306. cur=0
  307. fi
  308. pidfile="$PID_DIR/${user}__${pid}.last"
  309. if [[ -f "$pidfile" ]]; then
  310. prev=$(cat "$pidfile" 2>/dev/null)
  311. if ! [[ "$prev" =~ ^[0-9]+$ ]]; then prev=0; fi
  312. if [[ "$cur" -ge "$prev" ]]; then
  313. d=$((cur - prev))
  314. else
  315. d=$cur
  316. fi
  317. delta_total=$((delta_total + d))
  318. fi
  319. echo "$cur" > "$pidfile"
  320. done
  321. # Clean up dead PID files
  322. for f in "$PID_DIR/${user}__"*.last; do
  323. [[ ! -f "$f" ]] && continue
  324. fpid=$(basename "$f" .last)
  325. fpid=${fpid#${user}__}
  326. [[ ! -d "/proc/$fpid" ]] && rm -f "$f"
  327. done
  328. # Update total
  329. new_total=$((accumulated + delta_total))
  330. echo "$new_total" > "$usagefile"
  331. # Check quota
  332. quota_bytes=$(awk "BEGIN {printf \"%.0f\", $bandwidth_gb * 1073741824}")
  333. if [[ "$new_total" -ge "$quota_bytes" ]]; then
  334. if ! passwd -S "$user" 2>/dev/null | grep -q " L "; then
  335. usermod -L "$user" &>/dev/null
  336. killall -u "$user" -9 &>/dev/null
  337. fi
  338. fi
  339. done < "$DB_FILE"
  340. sleep 15
  341. done
  342. EOF
  343. chmod +x "$LIMITER_SCRIPT"
  344. # Strip DOS line endings in case menu.sh was uploaded from Windows
  345. sed -i 's/\r$//' "$LIMITER_SCRIPT" 2>/dev/null
  346. cat > "$LIMITER_SERVICE" << EOF
  347. [Unit]
  348. Description=FirewallFalcon Active User Limiter
  349. After=network.target
  350. [Service]
  351. Type=simple
  352. ExecStart=$LIMITER_SCRIPT
  353. Restart=always
  354. RestartSec=5
  355. [Install]
  356. WantedBy=multi-user.target
  357. EOF
  358. sed -i 's/\r$//' "$LIMITER_SERVICE" 2>/dev/null
  359. pkill -f "firewallfalcon-limiter" 2>/dev/null
  360. if ! systemctl is-active --quiet firewallfalcon-limiter; then
  361. systemctl daemon-reload
  362. systemctl enable firewallfalcon-limiter &>/dev/null
  363. systemctl start firewallfalcon-limiter --no-block &>/dev/null
  364. else
  365. systemctl restart firewallfalcon-limiter --no-block &>/dev/null
  366. fi
  367. }
  368. setup_bandwidth_service() {
  369. mkdir -p "$BANDWIDTH_DIR"
  370. # Bandwidth monitoring is now integrated into the limiter service above.
  371. # Stop the old standalone bandwidth service if it exists.
  372. if systemctl is-active --quiet firewallfalcon-bandwidth 2>/dev/null; then
  373. systemctl stop firewallfalcon-bandwidth &>/dev/null
  374. systemctl disable firewallfalcon-bandwidth &>/dev/null
  375. fi
  376. rm -f "$BANDWIDTH_SERVICE" "$BANDWIDTH_SCRIPT" 2>/dev/null
  377. }
  378. setup_trial_cleanup_script() {
  379. cat > "$TRIAL_CLEANUP_SCRIPT" << 'TREOF'
  380. #!/bin/bash
  381. # FirewallFalcon Trial Account Auto-Cleanup
  382. # Usage: firewallfalcon-trial-cleanup.sh <username>
  383. DB_FILE="/etc/firewallfalcon/users.db"
  384. BW_DIR="/etc/firewallfalcon/bandwidth"
  385. username="$1"
  386. if [[ -z "$username" ]]; then exit 1; fi
  387. # Kill active sessions
  388. killall -u "$username" -9 &>/dev/null
  389. sleep 1
  390. # Delete system user
  391. userdel -r "$username" &>/dev/null
  392. # Remove from DB
  393. sed -i "/^${username}:/d" "$DB_FILE"
  394. # Remove bandwidth tracking
  395. rm -f "$BW_DIR/${username}.usage"
  396. rm -rf "$BW_DIR/pidtrack/${username}"
  397. TREOF
  398. chmod +x "$TRIAL_CLEANUP_SCRIPT"
  399. }
  400. update_ssh_banners_config() {
  401. rm -f /usr/local/bin/firewallfalcon-login-info.sh 2>/dev/null
  402. if [[ ! -f "/etc/firewallfalcon/banners_enabled" ]]; then
  403. rm -f "$SSHD_FF_CONFIG" 2>/dev/null
  404. systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null
  405. return
  406. fi
  407. mkdir -p "/etc/firewallfalcon/banners" /etc/ssh/sshd_config.d
  408. tmp_conf="/tmp/ff_banners_new.conf"
  409. echo "# FirewallFalcon - Show login info native banners" > "$tmp_conf"
  410. if [[ -f "$DB_FILE" ]]; then
  411. while IFS=: read -r u _rest; do
  412. [[ -z "$u" || "$u" == \#* ]] && continue
  413. echo "Match User $u" >> "$tmp_conf"
  414. echo " Banner /etc/firewallfalcon/banners/${u}.txt" >> "$tmp_conf"
  415. done < "$DB_FILE"
  416. fi
  417. if ! cmp -s "$tmp_conf" "$SSHD_FF_CONFIG" 2>/dev/null; then
  418. mv "$tmp_conf" "$SSHD_FF_CONFIG"
  419. if ! grep -q "^Include /etc/ssh/sshd_config.d/" /etc/ssh/sshd_config 2>/dev/null; then
  420. echo "Include /etc/ssh/sshd_config.d/*.conf" >> /etc/ssh/sshd_config
  421. fi
  422. systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null
  423. else
  424. rm -f "$tmp_conf"
  425. fi
  426. }
  427. setup_ssh_login_info() {
  428. touch "/etc/firewallfalcon/banners_enabled"
  429. update_ssh_banners_config
  430. }
  431. generate_dns_record() {
  432. echo -e "\n${C_BLUE}⚙️ Generating a random domain...${C_RESET}"
  433. if ! command -v jq &> /dev/null; then
  434. echo -e "${C_YELLOW}⚠️ jq not found, attempting to install...${C_RESET}"
  435. apt-get update > /dev/null 2>&1 && apt-get install -y jq || {
  436. echo -e "${C_RED}❌ Failed to install jq. Cannot manage DNS records.${C_RESET}"
  437. return 1
  438. }
  439. fi
  440. local SERVER_IPV4
  441. SERVER_IPV4=$(curl -s -4 icanhazip.com)
  442. if ! _is_valid_ipv4 "$SERVER_IPV4"; then
  443. echo -e "\n${C_RED}❌ Error: Could not retrieve a valid public IPv4 address from icanhazip.com.${C_RESET}"
  444. echo -e "${C_YELLOW}ℹ️ Please check your server's network connection and DNS resolver settings.${C_RESET}"
  445. echo -e " Output received: '$SERVER_IPV4'"
  446. return 1
  447. fi
  448. local SERVER_IPV6
  449. SERVER_IPV6=$(curl -s -6 icanhazip.com --max-time 5)
  450. local RANDOM_SUBDOMAIN="vps-$(head /dev/urandom | tr -dc a-z0-9 | head -c 8)"
  451. local FULL_DOMAIN="$RANDOM_SUBDOMAIN.$DESEC_DOMAIN"
  452. local HAS_IPV6="false"
  453. local API_DATA
  454. API_DATA=$(printf '[{"subname": "%s", "type": "A", "ttl": 3600, "records": ["%s"]}]' "$RANDOM_SUBDOMAIN" "$SERVER_IPV4")
  455. if [[ -n "$SERVER_IPV6" ]]; then
  456. local aaaa_record
  457. aaaa_record=$(printf ',{"subname": "%s", "type": "AAAA", "ttl": 3600, "records": ["%s"]}' "$RANDOM_SUBDOMAIN" "$SERVER_IPV6")
  458. API_DATA="${API_DATA%?}${aaaa_record}]"
  459. HAS_IPV6="true"
  460. fi
  461. local CREATE_RESPONSE
  462. CREATE_RESPONSE=$(curl -s -w "%{http_code}" -X POST "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/" \
  463. -H "Authorization: Token $DESEC_TOKEN" -H "Content-Type: application/json" \
  464. --data "$API_DATA")
  465. local HTTP_CODE=${CREATE_RESPONSE: -3}
  466. local RESPONSE_BODY=${CREATE_RESPONSE:0:${#CREATE_RESPONSE}-3}
  467. if [[ "$HTTP_CODE" -ne 201 ]]; then
  468. echo -e "${C_RED}❌ Failed to create DNS records. API returned HTTP $HTTP_CODE.${C_RESET}"
  469. if ! echo "$RESPONSE_BODY" | jq . > /dev/null 2>&1; then
  470. echo "Raw Response: $RESPONSE_BODY"
  471. else
  472. echo "Response: $RESPONSE_BODY" | jq
  473. fi
  474. return 1
  475. fi
  476. cat > "$DNS_INFO_FILE" <<-EOF
  477. SUBDOMAIN="$RANDOM_SUBDOMAIN"
  478. FULL_DOMAIN="$FULL_DOMAIN"
  479. HAS_IPV6="$HAS_IPV6"
  480. EOF
  481. echo -e "\n${C_GREEN}✅ Successfully created domain: ${C_YELLOW}$FULL_DOMAIN${C_RESET}"
  482. }
  483. delete_dns_record() {
  484. if [ ! -f "$DNS_INFO_FILE" ]; then
  485. echo -e "\n${C_YELLOW}ℹ️ No domain to delete.${C_RESET}"
  486. return
  487. fi
  488. echo -e "\n${C_BLUE}🗑️ Deleting DNS records...${C_RESET}"
  489. source "$DNS_INFO_FILE"
  490. if [[ -z "$SUBDOMAIN" ]]; then
  491. echo -e "${C_RED}❌ Could not read record details from config file. Skipping deletion.${C_RESET}"
  492. return
  493. fi
  494. curl -s -X DELETE "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/$SUBDOMAIN/A/" \
  495. -H "Authorization: Token $DESEC_TOKEN" > /dev/null
  496. if [[ "$HAS_IPV6" == "true" ]]; then
  497. curl -s -X DELETE "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/$SUBDOMAIN/AAAA/" \
  498. -H "Authorization: Token $DESEC_TOKEN" > /dev/null
  499. fi
  500. echo -e "\n${C_GREEN}✅ Deleted domain: ${C_YELLOW}$FULL_DOMAIN${C_RESET}"
  501. rm -f "$DNS_INFO_FILE"
  502. }
  503. dns_menu() {
  504. clear; show_banner
  505. echo -e "${C_BOLD}${C_PURPLE}--- 🌐 DNS Domain Management ---${C_RESET}"
  506. if [ -f "$DNS_INFO_FILE" ]; then
  507. source "$DNS_INFO_FILE"
  508. echo -e "\nℹ️ A domain already exists for this server:"
  509. echo -e " - ${C_CYAN}Domain:${C_RESET} ${C_YELLOW}$FULL_DOMAIN${C_RESET}"
  510. echo
  511. read -p "👉 Do you want to DELETE this domain? (y/n): " choice
  512. if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
  513. delete_dns_record
  514. else
  515. echo -e "\n${C_YELLOW}❌ Action cancelled.${C_RESET}"
  516. fi
  517. else
  518. echo -e "\nℹ️ No domain has been generated for this server yet."
  519. echo
  520. read -p "👉 Do you want to generate a new random domain now? (y/n): " choice
  521. if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
  522. generate_dns_record
  523. else
  524. echo -e "\n${C_YELLOW}❌ Action cancelled.${C_RESET}"
  525. fi
  526. fi
  527. }
  528. _select_user_interface() {
  529. local title="$1"
  530. clear; show_banner
  531. echo -e "${C_BOLD}${C_PURPLE}${title}${C_RESET}\n"
  532. if [[ ! -s $DB_FILE ]]; then
  533. echo -e "${C_YELLOW}ℹ️ No users found in the database.${C_RESET}"
  534. SELECTED_USER="NO_USERS"; return
  535. fi
  536. mapfile -t all_users < <(cut -d: -f1 "$DB_FILE" | sort)
  537. if [ ${#all_users[@]} -ge 15 ]; then
  538. read -p "👉 Enter a search term (or press Enter to list all): " search_term
  539. if [[ -n "$search_term" ]]; then
  540. mapfile -t users < <(printf "%s\n" "${all_users[@]}" | grep -i "$search_term")
  541. else
  542. users=("${all_users[@]}")
  543. fi
  544. else
  545. users=("${all_users[@]}")
  546. fi
  547. if [ ${#users[@]} -eq 0 ]; then
  548. echo -e "\n${C_YELLOW}ℹ️ No users found matching your criteria.${C_RESET}"
  549. SELECTED_USER="NO_USERS"; return
  550. fi
  551. echo -e "\nPlease select a user:\n"
  552. for i in "${!users[@]}"; do
  553. printf " ${C_GREEN}[%2d]${C_RESET} %s\n" "$((i+1))" "${users[$i]}"
  554. done
  555. echo -e "\n ${C_RED} [ 0]${C_RESET} ↩️ Cancel and return to main menu"
  556. echo
  557. local choice
  558. while true; do
  559. read -p "👉 Enter the number of the user: " choice
  560. if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 0 ] && [ "$choice" -le "${#users[@]}" ]; then
  561. if [ "$choice" -eq 0 ]; then
  562. SELECTED_USER=""; return
  563. else
  564. SELECTED_USER="${users[$((choice-1))]}"; return
  565. fi
  566. else
  567. echo -e "${C_RED}❌ Invalid selection. Please try again.${C_RESET}"
  568. fi
  569. done
  570. }
  571. _select_multi_user_interface() {
  572. local title="$1"
  573. clear; show_banner
  574. echo -e "${C_BOLD}${C_PURPLE}${title}${C_RESET}\n"
  575. SELECTED_USERS=()
  576. if [[ ! -s $DB_FILE ]]; then
  577. echo -e "${C_YELLOW}ℹ️ No users found in the database.${C_RESET}"
  578. SELECTED_USERS=("NO_USERS"); return
  579. fi
  580. mapfile -t all_users < <(cut -d: -f1 "$DB_FILE" | sort)
  581. if [ ${#all_users[@]} -ge 15 ]; then
  582. read -p "👉 Enter a search term (or press Enter to list all): " search_term
  583. if [[ -n "$search_term" ]]; then
  584. mapfile -t users < <(printf "%s\n" "${all_users[@]}" | grep -i "$search_term")
  585. else
  586. users=("${all_users[@]}")
  587. fi
  588. else
  589. users=("${all_users[@]}")
  590. fi
  591. if [ ${#users[@]} -eq 0 ]; then
  592. echo -e "\n${C_YELLOW}ℹ️ No users found matching your criteria.${C_RESET}"
  593. SELECTED_USERS=("NO_USERS"); return
  594. fi
  595. echo -e "\nPlease select users:\n"
  596. for i in "${!users[@]}"; do
  597. printf " ${C_GREEN}[%2d]${C_RESET} %s\n" "$((i+1))" "${users[$i]}"
  598. done
  599. echo -e "\n ${C_GREEN}[all]${C_RESET} Select ALL listed users"
  600. echo -e " ${C_RED} [0]${C_RESET} ↩️ Cancel and return to main menu"
  601. echo -e "\n${C_CYAN}💡 You can select multiple! (e.g. '1 3 5' or '1,3' or '1-4')${C_RESET}"
  602. echo
  603. local choice
  604. while true; do
  605. read -p "👉 Enter user numbers: " choice
  606. choice=$(echo "$choice" | tr ',' ' ') # Replace commas with spaces
  607. if [[ -z "$choice" ]]; then
  608. echo -e "${C_RED}❌ Invalid selection. Please try again.${C_RESET}"
  609. continue
  610. fi
  611. if [[ "$choice" == "0" ]]; then
  612. SELECTED_USERS=(); return
  613. fi
  614. if [[ "${choice,,}" == "all" ]]; then
  615. SELECTED_USERS=("${users[@]}")
  616. return
  617. fi
  618. local valid=true
  619. local selected_indices=()
  620. for token in $choice; do
  621. if [[ "$token" =~ ^[0-9]+-[0-9]+$ ]]; then
  622. local start=${token%-*}
  623. local end=${token#*-}
  624. if [ "$start" -le "$end" ]; then
  625. for (( idx=start; idx<=end; idx++ )); do
  626. if [ "$idx" -ge 1 ] && [ "$idx" -le "${#users[@]}" ]; then
  627. selected_indices+=($idx)
  628. else
  629. valid=false; break
  630. fi
  631. done
  632. else
  633. valid=false; break
  634. fi
  635. elif [[ "$token" =~ ^[0-9]+$ ]]; then
  636. if [ "$token" -ge 1 ] && [ "$token" -le "${#users[@]}" ]; then
  637. selected_indices+=($token)
  638. else
  639. valid=false; break
  640. fi
  641. else
  642. valid=false; break
  643. fi
  644. done
  645. if [[ "$valid" == true && ${#selected_indices[@]} -gt 0 ]]; then
  646. mapfile -t unique_indices < <(printf "%s\n" "${selected_indices[@]}" | sort -u -n)
  647. for idx in "${unique_indices[@]}"; do
  648. SELECTED_USERS+=("${users[$((idx-1))]}")
  649. done
  650. return
  651. else
  652. echo -e "${C_RED}❌ Invalid selection. Please check your numbers.${C_RESET}"
  653. SELECTED_USERS=()
  654. selected_indices=()
  655. fi
  656. done
  657. }
  658. get_user_status() {
  659. local username="$1"
  660. if ! id "$username" &>/dev/null; then echo -e "${C_RED}Not Found${C_RESET}"; return; fi
  661. local expiry_date=$(grep "^$username:" "$DB_FILE" | cut -d: -f3)
  662. if passwd -S "$username" 2>/dev/null | grep -q " L "; then echo -e "${C_YELLOW}🔒 Locked${C_RESET}"; return; fi
  663. local expiry_ts=$(date -d "$expiry_date" +%s 2>/dev/null || echo 0)
  664. local current_ts=$(date +%s)
  665. if [[ $expiry_ts -lt $current_ts ]]; then echo -e "${C_RED}🗓️ Expired${C_RESET}"; return; fi
  666. echo -e "${C_GREEN}🟢 Active${C_RESET}"
  667. }
  668. create_user() {
  669. clear; show_banner
  670. echo -e "${C_BOLD}${C_PURPLE}--- ✨ Create New SSH User ---${C_RESET}"
  671. read -p "👉 Enter username (or '0' to cancel): " username
  672. if [[ "$username" == "0" ]]; then
  673. echo -e "\n${C_YELLOW}❌ User creation cancelled.${C_RESET}"
  674. return
  675. fi
  676. if [[ -z "$username" ]]; then
  677. echo -e "\n${C_RED}❌ Error: Username cannot be empty.${C_RESET}"
  678. return
  679. fi
  680. if id "$username" &>/dev/null || grep -q "^$username:" "$DB_FILE"; then
  681. echo -e "\n${C_RED}❌ Error: User '$username' already exists.${C_RESET}"; return
  682. fi
  683. local password=""
  684. while true; do
  685. read -p "🔑 Enter password (or press Enter for auto-generated): " password
  686. if [[ -z "$password" ]]; then
  687. password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
  688. echo -e "${C_GREEN}🔑 Auto-generated password: ${C_YELLOW}$password${C_RESET}"
  689. break
  690. else
  691. break
  692. fi
  693. done
  694. read -p "🗓️ Enter account duration (in days) [30]: " days
  695. days=${days:-30}
  696. if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  697. read -p "📶 Enter simultaneous connection limit [1]: " limit
  698. limit=${limit:-1}
  699. if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  700. read -p "📦 Enter bandwidth limit in GB (0 = unlimited) [0]: " bandwidth_gb
  701. bandwidth_gb=${bandwidth_gb:-0}
  702. if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  703. local expire_date
  704. expire_date=$(date -d "+$days days" +%Y-%m-%d)
  705. useradd -m -s /usr/sbin/nologin "$username"
  706. usermod -aG ffusers "$username" 2>/dev/null
  707. echo "$username:$password" | chpasswd; chage -E "$expire_date" "$username"
  708. echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
  709. local bw_display="Unlimited"
  710. if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
  711. clear; show_banner
  712. echo -e "${C_GREEN}✅ User '$username' created successfully!${C_RESET}\n"
  713. echo -e " - 👤 Username: ${C_YELLOW}$username${C_RESET}"
  714. echo -e " - 🔑 Password: ${C_YELLOW}$password${C_RESET}"
  715. echo -e " - 🗓️ Expires on: ${C_YELLOW}$expire_date${C_RESET}"
  716. echo -e " - 📶 Connection Limit: ${C_YELLOW}$limit${C_RESET}"
  717. echo -e " - 📦 Bandwidth Limit: ${C_YELLOW}$bw_display${C_RESET}"
  718. echo -e " ${C_DIM}(Active monitoring service will enforce these limits)${C_RESET}"
  719. # Auto-ask for config generation
  720. echo
  721. read -p "👉 Do you want to generate a client connection config for this user? (y/n): " gen_conf
  722. if [[ "$gen_conf" == "y" || "$gen_conf" == "Y" ]]; then
  723. generate_client_config "$username" "$password"
  724. fi
  725. update_ssh_banners_config
  726. }
  727. delete_user() {
  728. _select_multi_user_interface "--- 🗑️ Delete Users (from DB) ---"
  729. if [[ ${#SELECTED_USERS[@]} -eq 0 || "${SELECTED_USERS[0]}" == "NO_USERS" ]]; then return; fi
  730. echo -e "\n${C_RED}⚠️ You selected ${#SELECTED_USERS[@]} user(s) to delete: ${C_YELLOW}${SELECTED_USERS[*]}${C_RESET}"
  731. read -p "👉 Are you sure you want to PERMANENTLY delete them? (y/n): " confirm
  732. if [[ "$confirm" != "y" ]]; then echo -e "\n${C_YELLOW}❌ Deletion cancelled.${C_RESET}"; return; fi
  733. echo -e "\n${C_BLUE}🗑️ Deleting selected users...${C_RESET}"
  734. for username in "${SELECTED_USERS[@]}"; do
  735. killall -u "$username" -9 &>/dev/null
  736. sleep 0.2
  737. userdel -r "$username" &>/dev/null
  738. if [ $? -eq 0 ]; then
  739. echo -e " ✅ System user '${C_YELLOW}$username${C_RESET}' deleted."
  740. else
  741. echo -e " ❌ Failed to delete system user '${C_YELLOW}$username${C_RESET}'."
  742. fi
  743. rm -f "$BANDWIDTH_DIR/${username}.usage"
  744. rm -rf "$BANDWIDTH_DIR/pidtrack/${username}"
  745. sed -i "/^$username:/d" "$DB_FILE"
  746. done
  747. update_ssh_banners_config
  748. }
  749. edit_user() {
  750. _select_user_interface "--- ✏️ Edit a User ---"
  751. local username=$SELECTED_USER
  752. if [[ "$username" == "NO_USERS" ]] || [[ -z "$username" ]]; then return; fi
  753. while true; do
  754. clear; show_banner; echo -e "${C_BOLD}${C_PURPLE}--- Editing User: ${C_YELLOW}$username${C_PURPLE} ---${C_RESET}"
  755. # Show current user details
  756. local current_line; current_line=$(grep "^$username:" "$DB_FILE")
  757. local cur_pass; cur_pass=$(echo "$current_line" | cut -d: -f2)
  758. local cur_expiry; cur_expiry=$(echo "$current_line" | cut -d: -f3)
  759. local cur_limit; cur_limit=$(echo "$current_line" | cut -d: -f4)
  760. local cur_bw; cur_bw=$(echo "$current_line" | cut -d: -f5)
  761. [[ -z "$cur_bw" ]] && cur_bw="0"
  762. local cur_bw_display="Unlimited"; [[ "$cur_bw" != "0" ]] && cur_bw_display="${cur_bw} GB"
  763. # Show bandwidth usage
  764. local bw_used_display="N/A"
  765. if [[ -f "$BANDWIDTH_DIR/${username}.usage" ]]; then
  766. local used_bytes; used_bytes=$(cat "$BANDWIDTH_DIR/${username}.usage" 2>/dev/null)
  767. if [[ -n "$used_bytes" && "$used_bytes" != "0" ]]; then
  768. bw_used_display=$(awk "BEGIN {printf \"%.2f GB\", $used_bytes / 1073741824}")
  769. else
  770. bw_used_display="0.00 GB"
  771. fi
  772. fi
  773. 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}"
  774. echo -e "\nSelect a detail to edit:\n"
  775. printf " ${C_GREEN}[ 1]${C_RESET} %-35s\n" "🔑 Change Password"
  776. printf " ${C_GREEN}[ 2]${C_RESET} %-35s\n" "🗓️ Change Expiration Date"
  777. printf " ${C_GREEN}[ 3]${C_RESET} %-35s\n" "📶 Change Connection Limit"
  778. printf " ${C_GREEN}[ 4]${C_RESET} %-35s\n" "📦 Change Bandwidth Limit"
  779. printf " ${C_GREEN}[ 5]${C_RESET} %-35s\n" "🔄 Reset Bandwidth Counter"
  780. echo -e "\n ${C_RED}[ 0]${C_RESET} ✅ Finish Editing"; echo; read -p "👉 Enter your choice: " edit_choice
  781. case $edit_choice in
  782. 1)
  783. local new_pass=""
  784. read -p "Enter new password (or press Enter for auto-generated): " new_pass
  785. if [[ -z "$new_pass" ]]; then
  786. new_pass=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
  787. echo -e "${C_GREEN}🔑 Auto-generated: ${C_YELLOW}$new_pass${C_RESET}"
  788. fi
  789. echo "$username:$new_pass" | chpasswd
  790. sed -i "s/^$username:.*/$username:$new_pass:$cur_expiry:$cur_limit:$cur_bw/" "$DB_FILE"
  791. echo -e "\n${C_GREEN}✅ Password for '$username' changed to: ${C_YELLOW}$new_pass${C_RESET}"
  792. ;;
  793. 2) read -p "Enter new duration (in days from today): " days
  794. if [[ "$days" =~ ^[0-9]+$ ]]; then
  795. local new_expire_date; new_expire_date=$(date -d "+$days days" +%Y-%m-%d); chage -E "$new_expire_date" "$username"
  796. sed -i "s/^$username:.*/$username:$cur_pass:$new_expire_date:$cur_limit:$cur_bw/" "$DB_FILE"
  797. echo -e "\n${C_GREEN}✅ Expiration for '$username' set to ${C_YELLOW}$new_expire_date${C_RESET}."
  798. else echo -e "\n${C_RED}❌ Invalid number of days.${C_RESET}"; fi ;;
  799. 3) read -p "Enter new simultaneous connection limit: " new_limit
  800. if [[ "$new_limit" =~ ^[0-9]+$ ]]; then
  801. sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$new_limit:$cur_bw/" "$DB_FILE"
  802. echo -e "\n${C_GREEN}✅ Connection limit for '$username' set to ${C_YELLOW}$new_limit${C_RESET}."
  803. else echo -e "\n${C_RED}❌ Invalid limit.${C_RESET}"; fi ;;
  804. 4) read -p "Enter new bandwidth limit in GB (0 = unlimited): " new_bw
  805. if [[ "$new_bw" =~ ^[0-9]+\.?[0-9]*$ ]]; then
  806. sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$cur_limit:$new_bw/" "$DB_FILE"
  807. local bw_msg="Unlimited"; [[ "$new_bw" != "0" ]] && bw_msg="${new_bw} GB"
  808. echo -e "\n${C_GREEN}✅ Bandwidth limit for '$username' set to ${C_YELLOW}$bw_msg${C_RESET}."
  809. # Unlock user if they were locked due to bandwidth
  810. if [[ "$new_bw" == "0" ]] || [[ -f "$BANDWIDTH_DIR/${username}.usage" ]]; then
  811. local used_bytes; used_bytes=$(cat "$BANDWIDTH_DIR/${username}.usage" 2>/dev/null || echo 0)
  812. local new_quota_bytes; new_quota_bytes=$(awk "BEGIN {printf \"%.0f\", $new_bw * 1073741824}")
  813. if [[ "$new_bw" == "0" ]] || [[ "$used_bytes" -lt "$new_quota_bytes" ]]; then
  814. usermod -U "$username" &>/dev/null
  815. fi
  816. fi
  817. else echo -e "\n${C_RED}❌ Invalid bandwidth value.${C_RESET}"; fi ;;
  818. 5)
  819. echo "0" > "$BANDWIDTH_DIR/${username}.usage"
  820. # Unlock user if they were locked due to bandwidth
  821. usermod -U "$username" &>/dev/null
  822. echo -e "\n${C_GREEN}✅ Bandwidth counter for '$username' has been reset to 0.${C_RESET}"
  823. ;;
  824. 0) return ;;
  825. *) echo -e "\n${C_RED}❌ Invalid option.${C_RESET}" ;;
  826. esac
  827. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to continue editing..." && read -r
  828. done
  829. }
  830. lock_user() {
  831. _select_multi_user_interface "--- 🔒 Lock Users (from DB) ---"
  832. if [[ ${#SELECTED_USERS[@]} -eq 0 || "${SELECTED_USERS[0]}" == "NO_USERS" ]]; then return; fi
  833. echo -e "\n${C_BLUE}🔒 Locking selected users...${C_RESET}"
  834. for u in "${SELECTED_USERS[@]}"; do
  835. if ! id "$u" &>/dev/null; then
  836. echo -e " ❌ User '${C_YELLOW}$u${C_RESET}' does not exist on this system."
  837. continue
  838. fi
  839. usermod -L "$u"
  840. if [ $? -eq 0 ]; then
  841. killall -u "$u" -9 &>/dev/null
  842. echo -e " ✅ ${C_YELLOW}$u${C_RESET} locked and active sessions killed."
  843. else
  844. echo -e " ❌ Failed to lock ${C_YELLOW}$u${C_RESET}."
  845. fi
  846. done
  847. }
  848. unlock_user() {
  849. _select_multi_user_interface "--- 🔓 Unlock Users (from DB) ---"
  850. if [[ ${#SELECTED_USERS[@]} -eq 0 || "${SELECTED_USERS[0]}" == "NO_USERS" ]]; then return; fi
  851. echo -e "\n${C_BLUE}🔓 Unlocking selected users...${C_RESET}"
  852. for u in "${SELECTED_USERS[@]}"; do
  853. if ! id "$u" &>/dev/null; then
  854. echo -e " ❌ User '${C_YELLOW}$u${C_RESET}' does not exist on this system."
  855. continue
  856. fi
  857. usermod -U "$u"
  858. if [ $? -eq 0 ]; then
  859. echo -e " ✅ ${C_YELLOW}$u${C_RESET} unlocked."
  860. else
  861. echo -e " ❌ Failed to unlock ${C_YELLOW}$u${C_RESET}."
  862. fi
  863. done
  864. }
  865. list_users() {
  866. clear; show_banner
  867. if [[ ! -s "$DB_FILE" ]]; then
  868. echo -e "\n${C_YELLOW}ℹ️ No users are currently being managed.${C_RESET}"
  869. return
  870. fi
  871. echo -e "${C_BOLD}${C_PURPLE}--- 📋 Managed Users ---${C_RESET}"
  872. echo -e "${C_CYAN}=========================================================================================${C_RESET}"
  873. printf "${C_BOLD}${C_WHITE}%-18s | %-12s | %-10s | %-15s | %-20s${C_RESET}\n" "USERNAME" "EXPIRES" "CONNS" "BANDWIDTH" "STATUS"
  874. echo -e "${C_CYAN}-----------------------------------------------------------------------------------------${C_RESET}"
  875. while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
  876. local online_count
  877. online_count=$(pgrep -u "$user" sshd | wc -l)
  878. local status
  879. status=$(get_user_status "$user")
  880. local plain_status
  881. plain_status=$(echo -e "$status" | sed 's/\x1b\[[0-9;]*m//g')
  882. local connection_string="$online_count / $limit"
  883. # Bandwidth display
  884. [[ -z "$bandwidth_gb" ]] && bandwidth_gb="0"
  885. local bw_string="Unlimited"
  886. if [[ "$bandwidth_gb" != "0" ]]; then
  887. local used_bytes=0
  888. if [[ -f "$BANDWIDTH_DIR/${user}.usage" ]]; then
  889. used_bytes=$(cat "$BANDWIDTH_DIR/${user}.usage" 2>/dev/null)
  890. [[ -z "$used_bytes" ]] && used_bytes=0
  891. fi
  892. local used_gb
  893. used_gb=$(awk "BEGIN {printf \"%.1f\", $used_bytes / 1073741824}")
  894. bw_string="${used_gb}/${bandwidth_gb}GB"
  895. fi
  896. local line_color="$C_WHITE"
  897. case $plain_status in
  898. *"Active"*) line_color="$C_GREEN" ;;
  899. *"Locked"*) line_color="$C_YELLOW" ;;
  900. *"Expired"*) line_color="$C_RED" ;;
  901. *"Not Found"*) line_color="$C_DIM" ;;
  902. esac
  903. 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"
  904. done < <(sort "$DB_FILE")
  905. echo -e "${C_CYAN}=========================================================================================${C_RESET}\n"
  906. }
  907. renew_user() {
  908. _select_multi_user_interface "--- 🔄 Renew Users ---"
  909. if [[ ${#SELECTED_USERS[@]} -eq 0 || "${SELECTED_USERS[0]}" == "NO_USERS" ]]; then return; fi
  910. read -p "👉 Enter number of days to extend the account(s): " days; if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  911. local new_expire_date; new_expire_date=$(date -d "+$days days" +%Y-%m-%d)
  912. echo -e "\n${C_BLUE}🔄 Renewing selected users for $days days...${C_RESET}"
  913. for u in "${SELECTED_USERS[@]}"; do
  914. chage -E "$new_expire_date" "$u"
  915. 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)
  916. [[ -z "$bw" ]] && bw="0"
  917. sed -i "s/^$u:.*/$u:$pass:$new_expire_date:$limit:$bw/" "$DB_FILE"
  918. echo -e " ✅ ${C_YELLOW}$u${C_RESET} renewed until ${C_GREEN}${new_expire_date}${C_RESET}."
  919. done
  920. }
  921. cleanup_expired() {
  922. clear; show_banner
  923. echo -e "${C_BOLD}${C_PURPLE}--- 🧹 Cleanup Expired Users ---${C_RESET}"
  924. local expired_users=()
  925. local current_ts
  926. current_ts=$(date +%s)
  927. if [[ ! -s "$DB_FILE" ]]; then
  928. echo -e "\n${C_GREEN}✅ User database is empty. No expired users found.${C_RESET}"
  929. return
  930. fi
  931. while IFS=: read -r user pass expiry limit bandwidth_gb _extra; do
  932. local expiry_ts
  933. expiry_ts=$(date -d "$expiry" +%s 2>/dev/null || echo 0)
  934. if [[ $expiry_ts -lt $current_ts && $expiry_ts -ne 0 ]]; then
  935. expired_users+=("$user")
  936. fi
  937. done < "$DB_FILE"
  938. if [ ${#expired_users[@]} -eq 0 ]; then
  939. echo -e "\n${C_GREEN}✅ No expired users found.${C_RESET}"
  940. return
  941. fi
  942. echo -e "\nThe following users have expired: ${C_RED}${expired_users[*]}${C_RESET}"
  943. read -p "👉 Do you want to delete all of them? (y/n): " confirm
  944. if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
  945. for user in "${expired_users[@]}"; do
  946. echo " - Deleting ${C_YELLOW}$user...${C_RESET}"
  947. killall -u "$user" -9 &>/dev/null
  948. # Clean up bandwidth tracking
  949. rm -f "$BANDWIDTH_DIR/${user}.usage"
  950. rm -rf "$BANDWIDTH_DIR/pidtrack/${user}"
  951. userdel -r "$user" &>/dev/null
  952. sed -i "/^$user:/d" "$DB_FILE"
  953. done
  954. echo -e "\n${C_GREEN}✅ Expired users have been cleaned up.${C_RESET}"
  955. update_ssh_banners_config
  956. else
  957. echo -e "\n${C_YELLOW}❌ Cleanup cancelled.${C_RESET}"
  958. fi
  959. }
  960. backup_user_data() {
  961. clear; show_banner
  962. echo -e "${C_BOLD}${C_PURPLE}--- 💾 Backup User Data ---${C_RESET}"
  963. read -p "👉 Enter path for backup file [/root/firewallfalcon_users.tar.gz]: " backup_path
  964. backup_path=${backup_path:-/root/firewallfalcon_users.tar.gz}
  965. if [ ! -d "$DB_DIR" ] || [ ! -s "$DB_FILE" ]; then
  966. echo -e "\n${C_YELLOW}ℹ️ No user data found to back up.${C_RESET}"
  967. return
  968. fi
  969. echo -e "\n${C_BLUE}⚙️ Backing up user database and settings to ${C_YELLOW}$backup_path${C_RESET}..."
  970. tar -czf "$backup_path" -C "$(dirname "$DB_DIR")" "$(basename "$DB_DIR")"
  971. if [ $? -eq 0 ]; then
  972. echo -e "\n${C_GREEN}✅ SUCCESS: User data backup created at ${C_YELLOW}$backup_path${C_RESET}"
  973. else
  974. echo -e "\n${C_RED}❌ ERROR: Backup failed.${C_RESET}"
  975. fi
  976. }
  977. restore_user_data() {
  978. clear; show_banner
  979. echo -e "${C_BOLD}${C_PURPLE}--- 📥 Restore User Data ---${C_RESET}"
  980. read -p "👉 Enter the full path to the user data backup file [/root/firewallfalcon_users.tar.gz]: " backup_path
  981. backup_path=${backup_path:-/root/firewallfalcon_users.tar.gz}
  982. if [ ! -f "$backup_path" ]; then
  983. echo -e "\n${C_RED}❌ ERROR: Backup file not found at '$backup_path'.${C_RESET}"
  984. return
  985. fi
  986. echo -e "\n${C_RED}${C_BOLD}⚠️ WARNING:${C_RESET} This will overwrite all current users and settings."
  987. echo -e "It will restore user accounts, passwords, limits, and expiration dates from the backup file."
  988. read -p "👉 Are you absolutely sure you want to proceed? (y/n): " confirm
  989. if [[ "$confirm" != "y" ]]; then echo -e "\n${C_YELLOW}❌ Restore cancelled.${C_RESET}"; return; fi
  990. local temp_dir
  991. temp_dir=$(mktemp -d)
  992. echo -e "\n${C_BLUE}⚙️ Extracting backup file to a temporary location...${C_RESET}"
  993. tar -xzf "$backup_path" -C "$temp_dir"
  994. if [ $? -ne 0 ]; then
  995. echo -e "\n${C_RED}❌ ERROR: Failed to extract backup file. Aborting.${C_RESET}"
  996. rm -rf "$temp_dir"
  997. return
  998. fi
  999. local restored_db_file="$temp_dir/firewallfalcon/users.db"
  1000. if [ ! -f "$restored_db_file" ]; then
  1001. echo -e "\n${C_RED}❌ ERROR: users.db not found in the backup. Cannot restore user accounts.${C_RESET}"
  1002. rm -rf "$temp_dir"
  1003. return
  1004. fi
  1005. echo -e "${C_BLUE}⚙️ Overwriting current user database...${C_RESET}"
  1006. mkdir -p "$DB_DIR"
  1007. cp "$restored_db_file" "$DB_FILE"
  1008. if [ -d "$temp_dir/firewallfalcon/ssl" ]; then
  1009. cp -r "$temp_dir/firewallfalcon/ssl" "$DB_DIR/"
  1010. fi
  1011. if [ -d "$temp_dir/firewallfalcon/dnstt" ]; then
  1012. cp -r "$temp_dir/firewallfalcon/dnstt" "$DB_DIR/"
  1013. fi
  1014. if [ -f "$temp_dir/firewallfalcon/dns_info.conf" ]; then
  1015. cp "$temp_dir/firewallfalcon/dns_info.conf" "$DB_DIR/"
  1016. fi
  1017. if [ -f "$temp_dir/firewallfalcon/dnstt_info.conf" ]; then
  1018. cp "$temp_dir/firewallfalcon/dnstt_info.conf" "$DB_DIR/"
  1019. fi
  1020. if [ -f "$temp_dir/firewallfalcon/falconproxy_config.conf" ]; then
  1021. cp "$temp_dir/firewallfalcon/falconproxy_config.conf" "$DB_DIR/"
  1022. fi
  1023. echo -e "${C_BLUE}⚙️ Re-synchronizing system accounts with the restored database...${C_RESET}"
  1024. while IFS=: read -r user pass expiry limit; do
  1025. echo "Processing user: ${C_YELLOW}$user${C_RESET}"
  1026. if ! id "$user" &>/dev/null; then
  1027. echo " - User does not exist in system. Creating..."
  1028. useradd -m -s /usr/sbin/nologin "$user"
  1029. usermod -aG ffusers "$user" 2>/dev/null
  1030. fi
  1031. echo " - Setting password..."
  1032. echo "$user:$pass" | chpasswd
  1033. echo " - Setting expiration to $expiry..."
  1034. chage -E "$expiry" "$user"
  1035. echo " - Connection limit is $limit (enforced by PAM)"
  1036. done < "$DB_FILE"
  1037. rm -rf "$temp_dir"
  1038. echo -e "\n${C_GREEN}✅ SUCCESS: User data restore completed.${C_RESET}"
  1039. update_ssh_banners_config
  1040. }
  1041. _enable_banner_in_sshd_config() {
  1042. echo -e "\n${C_BLUE}⚙️ Configuring sshd_config...${C_RESET}"
  1043. sed -i.bak -E 's/^( *Banner *).*/#\1/' /etc/ssh/sshd_config
  1044. if ! grep -q -E "^Banner $SSH_BANNER_FILE" /etc/ssh/sshd_config; then
  1045. echo -e "\n# FirewallFalcon SSH Banner\nBanner $SSH_BANNER_FILE" >> /etc/ssh/sshd_config
  1046. fi
  1047. echo -e "${C_GREEN}✅ sshd_config updated.${C_RESET}"
  1048. }
  1049. _restart_ssh() {
  1050. echo -e "\n${C_BLUE}🔄 Restarting SSH service to apply changes...${C_RESET}"
  1051. local ssh_service_name=""
  1052. if [ -f /lib/systemd/system/sshd.service ]; then
  1053. ssh_service_name="sshd.service"
  1054. elif [ -f /lib/systemd/system/ssh.service ]; then
  1055. ssh_service_name="ssh.service"
  1056. else
  1057. echo -e "${C_RED}❌ Could not find sshd.service or ssh.service. Cannot restart SSH.${C_RESET}"
  1058. return 1
  1059. fi
  1060. systemctl restart "${ssh_service_name}"
  1061. if [ $? -eq 0 ]; then
  1062. echo -e "${C_GREEN}✅ SSH service ('${ssh_service_name}') restarted successfully.${C_RESET}"
  1063. else
  1064. echo -e "${C_RED}❌ Failed to restart SSH service ('${ssh_service_name}'). Please check 'journalctl -u ${ssh_service_name}' for errors.${C_RESET}"
  1065. fi
  1066. }
  1067. set_ssh_banner_paste() {
  1068. clear; show_banner
  1069. echo -e "${C_BOLD}${C_PURPLE}--- 📋 Paste SSH Banner ---${C_RESET}"
  1070. echo -e "Paste your banner code below. Press ${C_YELLOW}[Ctrl+D]${C_RESET} when you are finished."
  1071. echo -e "${C_DIM}The current banner (if any) will be overwritten.${C_RESET}"
  1072. echo -e "--------------------------------------------------"
  1073. cat > "$SSH_BANNER_FILE"
  1074. chmod 644 "$SSH_BANNER_FILE"
  1075. echo -e "\n--------------------------------------------------"
  1076. echo -e "\n${C_GREEN}✅ Banner content saved from paste.${C_RESET}"
  1077. _enable_banner_in_sshd_config
  1078. _restart_ssh
  1079. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to return..." && read -r
  1080. }
  1081. view_ssh_banner() {
  1082. clear; show_banner
  1083. echo -e "${C_BOLD}${C_PURPLE}--- 👁️ Current SSH Banner ---${C_RESET}"
  1084. if [ -f "$SSH_BANNER_FILE" ]; then
  1085. echo -e "\n${C_CYAN}--- BEGIN BANNER ---${C_RESET}"
  1086. cat "$SSH_BANNER_FILE"
  1087. echo -e "${C_CYAN}---- END BANNER ----${C_RESET}"
  1088. else
  1089. echo -e "\n${C_YELLOW}ℹ️ No banner file found at $SSH_BANNER_FILE.${C_RESET}"
  1090. fi
  1091. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to return..." && read -r
  1092. }
  1093. remove_ssh_banner() {
  1094. clear; show_banner
  1095. echo -e "${C_BOLD}${C_PURPLE}--- 🗑️ Remove SSH Banner ---${C_RESET}"
  1096. read -p "👉 Are you sure you want to disable and remove the SSH banner? (y/n): " confirm
  1097. if [[ "$confirm" != "y" ]]; then
  1098. echo -e "\n${C_YELLOW}❌ Action cancelled.${C_RESET}"
  1099. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to return..." && read -r
  1100. return
  1101. fi
  1102. if [ -f "$SSH_BANNER_FILE" ]; then
  1103. rm -f "$SSH_BANNER_FILE"
  1104. echo -e "\n${C_GREEN}✅ Removed banner file: $SSH_BANNER_FILE${C_RESET}"
  1105. else
  1106. echo -e "\n${C_YELLOW}ℹ️ No banner file to remove.${C_RESET}"
  1107. fi
  1108. echo -e "\n${C_BLUE}⚙️ Disabling banner in sshd_config...${C_RESET}"
  1109. sed -i.bak -E "s/^( *Banner\s+$SSH_BANNER_FILE)/#\1/" /etc/ssh/sshd_config
  1110. echo -e "${C_GREEN}✅ Banner disabled in configuration.${C_RESET}"
  1111. _restart_ssh
  1112. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to return..." && read -r
  1113. }
  1114. _install_certbot() {
  1115. if command -v certbot &> /dev/null; then
  1116. echo -e "${C_GREEN}✅ Certbot is already installed.${C_RESET}"
  1117. return 0
  1118. fi
  1119. echo -e "${C_YELLOW}⚠️ Certbot (for SSL) is not found.${C_RESET}"
  1120. read -p "👉 Do you want to install Certbot now? (y/n): " confirm_install
  1121. if [[ "$confirm_install" != "y" ]]; then
  1122. echo -e "${C_RED}❌ Installation skipped. Cannot proceed.${C_RESET}"
  1123. return 1
  1124. fi
  1125. echo -e "${C_BLUE}📦 Installing Certbot...${C_RESET}"
  1126. apt-get update > /dev/null 2>&1
  1127. apt-get install -y certbot || {
  1128. echo -e "${C_RED}❌ Failed to install Certbot.${C_RESET}"
  1129. return 1
  1130. }
  1131. echo -e "${C_GREEN}✅ Certbot installed successfully.${C_RESET}"
  1132. return 0
  1133. }
  1134. install_ssl_tunnel() {
  1135. clear; show_banner
  1136. echo -e "${C_BOLD}${C_PURPLE}--- 🚀 Installing Multiplexed HAProxy (Ports 80 & 443) ---${C_RESET}"
  1137. if ! command -v haproxy &> /dev/null; then
  1138. echo -e "\n${C_YELLOW}⚠️ HAProxy not found. Installing...${C_RESET}"
  1139. apt-get update && apt-get install -y haproxy || { echo -e "${C_RED}❌ Failed to install HAProxy.${C_RESET}"; return; }
  1140. fi
  1141. echo -e "\n${C_BLUE}🔍 Ensuring required ports (80, 443, 10443) are free and open...${C_RESET}"
  1142. check_and_free_ports "80" "443" "10443" || return
  1143. check_and_open_firewall_port "80" tcp || return
  1144. check_and_open_firewall_port "443" tcp || return
  1145. echo -e "\n${C_CYAN}Select SSL Certificate Type for HAProxy Decryption Engine:${C_RESET}"
  1146. echo -e " ${C_GREEN}[ 1]${C_RESET} Self-Signed Certificate (Default)"
  1147. echo -e " ${C_GREEN}[ 2]${C_RESET} Let's Encrypt Certificate (Certbot/Port 80 req.)"
  1148. read -p "👉 Enter choice [1]: " cert_choice
  1149. cert_choice=${cert_choice:-1}
  1150. mkdir -p "$SSL_CERT_DIR"
  1151. if [[ "$cert_choice" == "2" ]]; then
  1152. _install_certbot || return
  1153. read -p "👉 Enter your domain name (e.g., vps.example.com): " domain_name
  1154. read -p "👉 Enter your email address: " email
  1155. systemctl stop haproxy >/dev/null 2>&1
  1156. systemctl stop nginx >/dev/null 2>&1
  1157. certbot certonly --standalone -d "$domain_name" --non-interactive --agree-tos -m "$email"
  1158. if [ $? -eq 0 ]; then
  1159. echo -e "${C_GREEN}✅ Certificate obtained successfully! Creating HAProxy PEM...${C_RESET}"
  1160. cat "/etc/letsencrypt/live/$domain_name/fullchain.pem" "/etc/letsencrypt/live/$domain_name/privkey.pem" > "$SSL_CERT_FILE"
  1161. else
  1162. echo -e "${C_RED}❌ Certbot failed. Falling back to self-signed certificate.${C_RESET}"
  1163. cert_choice=1
  1164. fi
  1165. fi
  1166. if [[ "$cert_choice" == "1" ]]; then
  1167. if [ -f "$SSL_CERT_FILE" ]; then
  1168. read -p "👉 A certificate already exists. Overwrite? (y/n): " overwrite_cert
  1169. if [[ "$overwrite_cert" == "y" || "$overwrite_cert" == "Y" ]]; then
  1170. rm -f "$SSL_CERT_FILE"
  1171. fi
  1172. fi
  1173. if [ ! -f "$SSL_CERT_FILE" ]; then
  1174. echo -e "\n${C_GREEN}🔐 Generating internal loopback SSL certificate...${C_RESET}"
  1175. openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
  1176. -keyout "$SSL_CERT_FILE" -out "$SSL_CERT_FILE" \
  1177. -subj "/CN=@FIREWALLFALCON" \
  1178. >/dev/null 2>&1 || { echo -e "${C_RED}❌ Failed to generate SSL certificate.${C_RESET}"; return; }
  1179. fi
  1180. fi
  1181. echo -e "\n${C_GREEN}📝 Applying Multiplexed HAProxy configuration...${C_RESET}"
  1182. cat > "$HAPROXY_CONFIG" << 'EOF'
  1183. global
  1184. log /dev/log local0
  1185. log /dev/log local1 notice
  1186. chroot /var/lib/haproxy
  1187. stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
  1188. stats timeout 30s
  1189. user haproxy
  1190. group haproxy
  1191. daemon
  1192. defaults
  1193. log global
  1194. mode tcp
  1195. option tcplog
  1196. option dontlognull
  1197. timeout connect 5s
  1198. timeout client 24h
  1199. timeout server 24h
  1200. # ====================================================================
  1201. # TIER 1: PORT 80 (Cleartext Payloads & Raw SSH)
  1202. # ====================================================================
  1203. frontend port_80_edge
  1204. bind *:80
  1205. mode tcp
  1206. tcp-request inspect-delay 2s
  1207. # Check for SSH Hex
  1208. acl is_ssh payload(0,7) -m bin 5353482d322e30
  1209. tcp-request content accept if is_ssh
  1210. tcp-request content accept if HTTP
  1211. # 1. Raw SSH -> Port 22
  1212. use_backend direct_ssh if is_ssh
  1213. # 2. Cleartext HTTP Payload -> Nginx 8880 -> Your 8080 Proxy
  1214. default_backend nginx_cleartext
  1215. # ====================================================================
  1216. # TIER 1: PORT 443 (TLS v2ray, SSL Payloads, Raw SSH)
  1217. # ====================================================================
  1218. frontend port_443_edge
  1219. bind *:443
  1220. mode tcp
  1221. tcp-request inspect-delay 2s
  1222. acl is_ssh payload(0,7) -m bin 5353482d322e30
  1223. acl is_tls req.ssl_hello_type 1
  1224. # Check ALPN before decryption to save v2ray!
  1225. acl has_web_alpn req.ssl_alpn -m sub h2 http/1.1
  1226. tcp-request content accept if is_ssh
  1227. tcp-request content accept if HTTP
  1228. tcp-request content accept if is_tls
  1229. # 1. Raw SSH directly on 443 -> Port 22
  1230. use_backend direct_ssh if is_ssh
  1231. # 2. Cleartext Payload dropped on 443 -> Nginx 8880
  1232. use_backend nginx_cleartext if HTTP
  1233. # 3. TLS with Web ALPN (v2ray / Web Payload) -> Pass RAW TLS to Nginx 8443!
  1234. use_backend nginx_tls if is_tls has_web_alpn
  1235. # 4. TLS without ALPN (Any-SNI Stunnel/SSH-TLS) -> Decrypt internally
  1236. default_backend loopback_ssl_terminator
  1237. # ====================================================================
  1238. # TIER 2: INTERNAL DECRYPTOR (Only for Any-SNI SSH-TLS)
  1239. # ====================================================================
  1240. frontend internal_decryptor
  1241. bind 127.0.0.1:10443 ssl crt /etc/firewallfalcon/ssl/firewallfalcon.pem
  1242. mode tcp
  1243. tcp-request inspect-delay 2s
  1244. acl is_ssh payload(0,7) -m bin 5353482d322e30
  1245. tcp-request content accept if is_ssh
  1246. tcp-request content accept if HTTP
  1247. # 1. Inside the tunnel is pure SSH -> Port 22
  1248. use_backend direct_ssh if is_ssh
  1249. # 2. Inside the tunnel is a Payload -> Nginx 8880
  1250. default_backend nginx_cleartext
  1251. # ====================================================================
  1252. # DESTINATION BACKENDS (Clean handoffs, no proxy headers)
  1253. # ====================================================================
  1254. backend direct_ssh
  1255. mode tcp
  1256. server ssh_server 127.0.0.1:22
  1257. backend nginx_cleartext
  1258. mode tcp
  1259. server nginx_8880 127.0.0.1:8880
  1260. backend nginx_tls
  1261. mode tcp
  1262. server nginx_8443 127.0.0.1:8443
  1263. backend loopback_ssl_terminator
  1264. mode tcp
  1265. server haproxy_ssl 127.0.0.1:10443
  1266. EOF
  1267. echo -e "\n${C_GREEN}▶️ Reloading and starting HAProxy service...${C_RESET}"
  1268. systemctl daemon-reload
  1269. systemctl restart haproxy
  1270. sleep 2
  1271. if systemctl is-active --quiet haproxy; then
  1272. echo -e "\n${C_GREEN}✅ SUCCESS: Multiplexed HAProxy Tunnel is active.${C_RESET}"
  1273. echo -e "Clients can now connect to this server's IP directly on port ${C_YELLOW}80 or 443${C_RESET}."
  1274. else
  1275. echo -e "\n${C_RED}❌ ERROR: HAProxy service failed to start.${C_RESET}"
  1276. systemctl status haproxy --no-pager
  1277. fi
  1278. }
  1279. uninstall_ssl_tunnel() {
  1280. echo -e "\n${C_BOLD}${C_PURPLE}--- 🗑️ Uninstalling HAProxy ---${C_RESET}"
  1281. if ! command -v haproxy &> /dev/null; then
  1282. echo -e "${C_YELLOW}ℹ️ HAProxy not installed, skipping.${C_RESET}"
  1283. return
  1284. fi
  1285. echo -e "${C_GREEN}🛑 Stopping HAProxy service...${C_RESET}"
  1286. systemctl stop haproxy >/dev/null 2>&1
  1287. if [ -f "$HAPROXY_CONFIG" ]; then
  1288. echo -e "${C_GREEN}📝 Restoring default/empty HAProxy config...${C_RESET}"
  1289. cat > "$HAPROXY_CONFIG" <<-EOF
  1290. global
  1291. log /dev/log local0
  1292. log /dev/log local1 notice
  1293. defaults
  1294. log global
  1295. EOF
  1296. fi
  1297. if [ -f "$SSL_CERT_FILE" ]; then
  1298. local delete_cert="y"
  1299. if [[ "$UNINSTALL_MODE" != "silent" ]]; then
  1300. read -p "👉 Delete the SSL certificate at $SSL_CERT_FILE? (y/n): " delete_cert
  1301. fi
  1302. if [[ "$delete_cert" == "y" ]]; then
  1303. echo -e "${C_GREEN}🗑️ Removing SSL certificate...${C_RESET}"
  1304. rm -f "$SSL_CERT_FILE"
  1305. fi
  1306. fi
  1307. echo -e "${C_GREEN}✅ HAProxy Tunnel has been uninstalled.${C_RESET}"
  1308. }
  1309. show_dnstt_details() {
  1310. if [ -f "$DNSTT_CONFIG_FILE" ]; then
  1311. source "$DNSTT_CONFIG_FILE"
  1312. echo -e "\n${C_GREEN}=====================================================${C_RESET}"
  1313. echo -e "${C_GREEN} 📡 DNSTT Connection Details ${C_RESET}"
  1314. echo -e "${C_GREEN}=====================================================${C_RESET}"
  1315. echo -e "\n${C_WHITE}Your connection details:${C_RESET}"
  1316. echo -e " - ${C_CYAN}Tunnel Domain:${C_RESET} ${C_YELLOW}$TUNNEL_DOMAIN${C_RESET}"
  1317. echo -e " - ${C_CYAN}Public Key:${C_RESET} ${C_YELLOW}$PUBLIC_KEY${C_RESET}"
  1318. if [[ -n "$FORWARD_DESC" ]]; then
  1319. echo -e " - ${C_CYAN}Forwarding To:${C_RESET} ${C_YELLOW}$FORWARD_DESC${C_RESET}"
  1320. else
  1321. echo -e " - ${C_CYAN}Forwarding To:${C_RESET} ${C_YELLOW}Unknown (config_missing)${C_RESET}"
  1322. fi
  1323. if [[ -n "$MTU_VALUE" ]]; then
  1324. echo -e " - ${C_CYAN}MTU Value:${C_RESET} ${C_YELLOW}$MTU_VALUE${C_RESET}"
  1325. fi
  1326. if [[ "$DNSTT_RECORDS_MANAGED" == "false" && -n "$NS_DOMAIN" ]]; then
  1327. echo -e " - ${C_CYAN}NS Record:${C_RESET} ${C_YELLOW}$NS_DOMAIN${C_RESET}"
  1328. fi
  1329. if [[ "$FORWARD_DESC" == *"V2Ray"* ]]; then
  1330. echo -e " - ${C_CYAN}Action Required:${C_RESET} ${C_YELLOW}Ensure a V2Ray service (vless/vmess/trojan) listens on port 8787 (no TLS)${C_RESET}"
  1331. elif [[ "$FORWARD_DESC" == *"SSH"* ]]; then
  1332. echo -e " - ${C_CYAN}Action Required:${C_RESET} ${C_YELLOW}Ensure your SSH client is configured to use the DNS tunnel.${C_RESET}"
  1333. fi
  1334. echo -e "\n${C_DIM}Use these details in your client configuration.${C_RESET}"
  1335. else
  1336. echo -e "\n${C_YELLOW}ℹ️ DNSTT configuration file not found. Details are unavailable.${C_RESET}"
  1337. fi
  1338. }
  1339. install_dnstt() {
  1340. clear; show_banner
  1341. echo -e "${C_BOLD}${C_PURPLE}--- 📡 DNSTT (DNS Tunnel) Management ---${C_RESET}"
  1342. if [ -f "$DNSTT_SERVICE_FILE" ]; then
  1343. echo -e "\n${C_YELLOW}ℹ️ DNSTT is already installed.${C_RESET}"
  1344. show_dnstt_details
  1345. return
  1346. fi
  1347. # --- FIX: Force release of Port 53 / Disable systemd-resolved ---
  1348. echo -e "${C_GREEN}⚙️ Forcing release of Port 53 (stopping systemd-resolved)...${C_RESET}"
  1349. systemctl stop systemd-resolved >/dev/null 2>&1
  1350. systemctl disable systemd-resolved >/dev/null 2>&1
  1351. rm -f /etc/resolv.conf
  1352. echo "nameserver 8.8.8.8" | tee /etc/resolv.conf > /dev/null
  1353. # ----------------------------------------------------------------
  1354. echo -e "\n${C_BLUE}🔎 Checking if port 53 (UDP) is available...${C_RESET}"
  1355. if ss -lunp | grep -q ':53\s'; then
  1356. if [[ $(ps -p $(ss -lunp | grep ':53\s' | grep -oP 'pid=\K[0-9]+') -o comm=) == "systemd-resolve" ]]; then
  1357. echo -e "${C_YELLOW}⚠️ Warning: Port 53 is in use by 'systemd-resolved'.${C_RESET}"
  1358. echo -e "${C_YELLOW}This is the system's DNS stub resolver. It must be disabled to run DNSTT.${C_RESET}"
  1359. read -p "👉 Allow the script to automatically disable it and reconfigure DNS? (y/n): " resolve_confirm
  1360. if [[ "$resolve_confirm" == "y" || "$resolve_confirm" == "Y" ]]; then
  1361. echo -e "${C_GREEN}⚙️ Stopping and disabling systemd-resolved to free port 53...${C_RESET}"
  1362. systemctl stop systemd-resolved
  1363. systemctl disable systemd-resolved
  1364. chattr -i /etc/resolv.conf &>/dev/null
  1365. rm -f /etc/resolv.conf
  1366. echo "nameserver 8.8.8.8" > /etc/resolv.conf
  1367. chattr +i /etc/resolv.conf
  1368. echo -e "${C_GREEN}✅ Port 53 has been freed and DNS set to 8.8.8.8.${C_RESET}"
  1369. else
  1370. echo -e "${C_RED}❌ Cannot proceed without freeing port 53. Aborting.${C_RESET}"
  1371. return
  1372. fi
  1373. else
  1374. check_and_free_ports "53" || return
  1375. fi
  1376. else
  1377. echo -e "${C_GREEN}✅ Port 53 (UDP) is free to use.${C_RESET}"
  1378. fi
  1379. check_and_open_firewall_port 53 udp || return
  1380. local forward_port=""
  1381. local forward_desc=""
  1382. echo -e "\n${C_BLUE}Please choose where DNSTT should forward traffic:${C_RESET}"
  1383. echo -e " ${C_GREEN}[ 1]${C_RESET} ➡️ Forward to local SSH service (port 22)"
  1384. echo -e " ${C_GREEN}[ 2]${C_RESET} ➡️ Forward to local V2Ray backend (port 8787)"
  1385. read -p "👉 Enter your choice [2]: " fwd_choice
  1386. fwd_choice=${fwd_choice:-2}
  1387. if [[ "$fwd_choice" == "1" ]]; then
  1388. forward_port="22"
  1389. forward_desc="SSH (port 22)"
  1390. echo -e "${C_GREEN}ℹ️ DNSTT will forward to SSH on 127.0.0.1:22.${C_RESET}"
  1391. elif [[ "$fwd_choice" == "2" ]]; then
  1392. forward_port="8787"
  1393. forward_desc="V2Ray (port 8787)"
  1394. echo -e "${C_GREEN}ℹ️ DNSTT will forward to V2Ray on 127.0.0.1:8787.${C_RESET}"
  1395. else
  1396. echo -e "${C_RED}❌ Invalid choice. Aborting.${C_RESET}"
  1397. return
  1398. fi
  1399. local FORWARD_TARGET="127.0.0.1:$forward_port"
  1400. local NS_DOMAIN=""
  1401. local TUNNEL_DOMAIN=""
  1402. local DNSTT_RECORDS_MANAGED="true"
  1403. local NS_SUBDOMAIN=""
  1404. local TUNNEL_SUBDOMAIN=""
  1405. local HAS_IPV6="false"
  1406. read -p "👉 Auto-generate DNS records or use custom ones? (auto/custom) [auto]: " dns_choice
  1407. dns_choice=${dns_choice:-auto}
  1408. if [[ "$dns_choice" == "custom" ]]; then
  1409. DNSTT_RECORDS_MANAGED="false"
  1410. read -p "👉 Enter your full nameserver domain (e.g., ns1.yourdomain.com): " NS_DOMAIN
  1411. if [[ -z "$NS_DOMAIN" ]]; then echo -e "\n${C_RED}❌ Nameserver domain cannot be empty. Aborting.${C_RESET}"; return; fi
  1412. read -p "👉 Enter your full tunnel domain (e.g., tun.yourdomain.com): " TUNNEL_DOMAIN
  1413. if [[ -z "$TUNNEL_DOMAIN" ]]; then echo -e "\n${C_RED}❌ Tunnel domain cannot be empty. Aborting.${C_RESET}"; return; fi
  1414. else
  1415. echo -e "\n${C_BLUE}⚙️ Configuring DNS records for DNSTT...${C_RESET}"
  1416. local SERVER_IPV4
  1417. SERVER_IPV4=$(curl -s -4 icanhazip.com)
  1418. if ! _is_valid_ipv4 "$SERVER_IPV4"; then
  1419. echo -e "\n${C_RED}❌ Error: Could not retrieve a valid public IPv4 address from icanhazip.com.${C_RESET}"
  1420. echo -e "${C_YELLOW}ℹ️ Please check your server's network connection and DNS resolver settings.${C_RESET}"
  1421. echo -e " Output received: '$SERVER_IPV4'"
  1422. return 1
  1423. fi
  1424. local SERVER_IPV6
  1425. SERVER_IPV6=$(curl -s -6 icanhazip.com --max-time 5)
  1426. local RANDOM_STR
  1427. RANDOM_STR=$(head /dev/urandom | tr -dc a-z0-9 | head -c 6)
  1428. NS_SUBDOMAIN="ns-$RANDOM_STR"
  1429. TUNNEL_SUBDOMAIN="tun-$RANDOM_STR"
  1430. NS_DOMAIN="$NS_SUBDOMAIN.$DESEC_DOMAIN"
  1431. TUNNEL_DOMAIN="$TUNNEL_DOMAIN.$DESEC_DOMAIN"
  1432. local API_DATA
  1433. API_DATA=$(printf '[{"subname": "%s", "type": "A", "ttl": 3600, "records": ["%s"]}, {"subname": "%s", "type": "NS", "ttl": 3600, "records": ["%s."]}]' \
  1434. "$NS_SUBDOMAIN" "$SERVER_IPV4" "$TUNNEL_SUBDOMAIN" "$NS_DOMAIN")
  1435. if [[ -n "$SERVER_IPV6" ]]; then
  1436. local aaaa_record
  1437. aaaa_record=$(printf ',{"subname": "%s", "type": "AAAA", "ttl": 3600, "records": ["%s"]}' "$NS_SUBDOMAIN" "$SERVER_IPV6")
  1438. API_DATA="${API_DATA%?}${aaaa_record}]"
  1439. HAS_IPV6="true"
  1440. fi
  1441. local CREATE_RESPONSE
  1442. CREATE_RESPONSE=$(curl -s -w "%{http_code}" -X POST "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/" \
  1443. -H "Authorization: Token $DESEC_TOKEN" -H "Content-Type: application/json" \
  1444. --data "$API_DATA")
  1445. local HTTP_CODE=${CREATE_RESPONSE: -3}
  1446. local RESPONSE_BODY=${CREATE_RESPONSE:0:${#CREATE_RESPONSE}-3}
  1447. if [[ "$HTTP_CODE" -ne 201 ]]; then
  1448. echo -e "${C_RED}❌ Failed to create DNSTT records. API returned HTTP $HTTP_CODE.${C_RESET}"
  1449. echo "Response: $RESPONSE_BODY" | jq
  1450. return 1
  1451. fi
  1452. fi
  1453. read -p "👉 Enter MTU value (e.g., 512, 1200) or press [Enter] for default: " mtu_value
  1454. local mtu_string=""
  1455. if [[ "$mtu_value" =~ ^[0-9]+$ ]]; then
  1456. mtu_string=" -mtu $mtu_value"
  1457. echo -e "${C_GREEN}ℹ️ Using MTU: $mtu_value${C_RESET}"
  1458. else
  1459. mtu_value=""
  1460. echo -e "${C_YELLOW}ℹ️ Using default MTU.${C_RESET}"
  1461. fi
  1462. echo -e "\n${C_BLUE}📥 Downloading pre-compiled DNSTT server binary...${C_RESET}"
  1463. local arch
  1464. arch=$(uname -m)
  1465. local binary_url=""
  1466. if [[ "$arch" == "x86_64" ]]; then
  1467. binary_url="https://dnstt.network/dnstt-server-linux-amd64"
  1468. echo -e "${C_BLUE}ℹ️ Detected x86_64 (amd64) architecture.${C_RESET}"
  1469. elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then
  1470. binary_url="https://dnstt.network/dnstt-server-linux-arm64"
  1471. echo -e "${C_BLUE}ℹ️ Detected ARM64 architecture.${C_RESET}"
  1472. else
  1473. echo -e "\n${C_RED}❌ Unsupported architecture: $arch. Cannot install DNSTT.${C_RESET}"
  1474. return
  1475. fi
  1476. curl -sL "$binary_url" -o "$DNSTT_BINARY"
  1477. if [ $? -ne 0 ]; then
  1478. echo -e "\n${C_RED}❌ Failed to download the DNSTT binary.${C_RESET}"
  1479. return
  1480. fi
  1481. chmod +x "$DNSTT_BINARY"
  1482. echo -e "${C_BLUE}🔐 Generating cryptographic keys...${C_RESET}"
  1483. mkdir -p "$DNSTT_KEYS_DIR"
  1484. "$DNSTT_BINARY" -gen-key -privkey-file "$DNSTT_KEYS_DIR/server.key" -pubkey-file "$DNSTT_KEYS_DIR/server.pub"
  1485. if [[ ! -f "$DNSTT_KEYS_DIR/server.key" ]]; then echo -e "${C_RED}❌ Failed to generate DNSTT keys.${C_RESET}"; return; fi
  1486. local PUBLIC_KEY
  1487. PUBLIC_KEY=$(cat "$DNSTT_KEYS_DIR/server.pub")
  1488. echo -e "\n${C_BLUE}📝 Creating systemd service...${C_RESET}"
  1489. cat > "$DNSTT_SERVICE_FILE" <<-EOF
  1490. [Unit]
  1491. Description=DNSTT (DNS Tunnel) Server for $forward_desc
  1492. After=network.target
  1493. [Service]
  1494. Type=simple
  1495. User=root
  1496. ExecStart=$DNSTT_BINARY -udp :53$mtu_string -privkey-file $DNSTT_KEYS_DIR/server.key $TUNNEL_DOMAIN $FORWARD_TARGET
  1497. Restart=always
  1498. RestartSec=3
  1499. [Install]
  1500. WantedBy=multi-user.target
  1501. EOF
  1502. echo -e "\n${C_BLUE}💾 Saving configuration and starting service...${C_RESET}"
  1503. cat > "$DNSTT_CONFIG_FILE" <<-EOF
  1504. NS_SUBDOMAIN="$NS_SUBDOMAIN"
  1505. TUNNEL_SUBDOMAIN="$TUNNEL_SUBDOMAIN"
  1506. NS_DOMAIN="$NS_DOMAIN"
  1507. TUNNEL_DOMAIN="$TUNNEL_DOMAIN"
  1508. PUBLIC_KEY="$PUBLIC_KEY"
  1509. FORWARD_DESC="$forward_desc"
  1510. DNSTT_RECORDS_MANAGED="$DNSTT_RECORDS_MANAGED"
  1511. HAS_IPV6="$HAS_IPV6"
  1512. MTU_VALUE="$mtu_value"
  1513. EOF
  1514. systemctl daemon-reload
  1515. systemctl enable dnstt.service
  1516. systemctl start dnstt.service
  1517. sleep 2
  1518. if systemctl is-active --quiet dnstt.service; then
  1519. echo -e "\n${C_GREEN}✅ SUCCESS: DNSTT has been installed and started!${C_RESET}"
  1520. show_dnstt_details
  1521. else
  1522. echo -e "\n${C_RED}❌ ERROR: DNSTT service failed to start.${C_RESET}"
  1523. journalctl -u dnstt.service -n 15 --no-pager
  1524. fi
  1525. }
  1526. uninstall_dnstt() {
  1527. echo -e "\n${C_BOLD}${C_PURPLE}--- 🗑️ Uninstalling DNSTT ---${C_RESET}"
  1528. if [ ! -f "$DNSTT_SERVICE_FILE" ]; then
  1529. echo -e "${C_YELLOW}ℹ️ DNSTT does not appear to be installed, skipping.${C_RESET}"
  1530. return
  1531. fi
  1532. local confirm="y"
  1533. if [[ "$UNINSTALL_MODE" != "silent" ]]; then
  1534. read -p "👉 Are you sure you want to uninstall DNSTT? This will delete DNS records if they were auto-generated. (y/n): " confirm
  1535. fi
  1536. if [[ "$confirm" != "y" ]]; then
  1537. echo -e "\n${C_YELLOW}❌ Uninstallation cancelled.${C_RESET}"
  1538. return
  1539. fi
  1540. echo -e "${C_BLUE}🛑 Stopping and disabling DNSTT service...${C_RESET}"
  1541. systemctl stop dnstt.service > /dev/null 2>&1
  1542. systemctl disable dnstt.service > /dev/null 2>&1
  1543. if [ -f "$DNSTT_CONFIG_FILE" ]; then
  1544. source "$DNSTT_CONFIG_FILE"
  1545. if [[ "$DNSTT_RECORDS_MANAGED" == "true" ]]; then
  1546. echo -e "${C_BLUE}🗑️ Removing auto-generated DNS records...${C_RESET}"
  1547. curl -s -X DELETE "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/$TUNNEL_SUBDOMAIN/NS/" \
  1548. -H "Authorization: Token $DESEC_TOKEN" > /dev/null
  1549. curl -s -X DELETE "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/$NS_SUBDOMAIN/A/" \
  1550. -H "Authorization: Token $DESEC_TOKEN" > /dev/null
  1551. if [[ "$HAS_IPV6" == "true" ]]; then
  1552. curl -s -X DELETE "https://desec.io/api/v1/domains/$DESEC_DOMAIN/rrsets/$NS_SUBDOMAIN/AAAA/" \
  1553. -H "Authorization: Token $DESEC_TOKEN" > /dev/null
  1554. fi
  1555. echo -e "${C_GREEN}✅ DNS records have been removed.${C_RESET}"
  1556. else
  1557. echo -e "${C_YELLOW}⚠️ DNS records were manually configured. Please delete them from your DNS provider.${C_RESET}"
  1558. fi
  1559. fi
  1560. echo -e "${C_BLUE}🗑️ Removing service files and binaries...${C_RESET}"
  1561. rm -f "$DNSTT_SERVICE_FILE"
  1562. rm -f "$DNSTT_BINARY"
  1563. rm -rf "$DNSTT_KEYS_DIR"
  1564. rm -f "$DNSTT_CONFIG_FILE"
  1565. systemctl daemon-reload
  1566. echo -e "${C_YELLOW}ℹ️ Making /etc/resolv.conf writable again...${C_RESET}"
  1567. chattr -i /etc/resolv.conf &>/dev/null
  1568. echo -e "\n${C_GREEN}✅ DNSTT has been successfully uninstalled.${C_RESET}"
  1569. }
  1570. install_falcon_proxy() {
  1571. clear; show_banner
  1572. echo -e "${C_BOLD}${C_PURPLE}--- 🦅 Installing Falcon Proxy (Websockets/Socks) ---${C_RESET}"
  1573. if [ -f "$FALCONPROXY_SERVICE_FILE" ]; then
  1574. echo -e "\n${C_YELLOW}ℹ️ Falcon Proxy is already installed.${C_RESET}"
  1575. if [ -f "$FALCONPROXY_CONFIG_FILE" ]; then
  1576. source "$FALCONPROXY_CONFIG_FILE"
  1577. echo -e " It is configured to run on port(s): ${C_YELLOW}$PORTS${C_RESET}"
  1578. echo -e " Installed Version: ${C_YELLOW}${INSTALLED_VERSION:-Unknown}${C_RESET}"
  1579. fi
  1580. read -p "👉 Do you want to reinstall/update? (y/n): " confirm_reinstall
  1581. if [[ "$confirm_reinstall" != "y" ]]; then return; fi
  1582. fi
  1583. echo -e "\n${C_BLUE}🌐 Fetching available versions from GitHub...${C_RESET}"
  1584. local releases_json=$(curl -s "https://api.github.com/repos/firewallfalcons/FirewallFalcon-Manager/releases")
  1585. if [[ -z "$releases_json" || "$releases_json" == "[]" ]]; then
  1586. echo -e "${C_RED}❌ Error: Could not fetch releases. Check internet or API limits.${C_RESET}"
  1587. return
  1588. fi
  1589. # Extract tag names
  1590. mapfile -t versions < <(echo "$releases_json" | jq -r '.[].tag_name')
  1591. if [ ${#versions[@]} -eq 0 ]; then
  1592. echo -e "${C_RED}❌ No releases found in the repository.${C_RESET}"
  1593. return
  1594. fi
  1595. echo -e "\n${C_CYAN}Select a version to install:${C_RESET}"
  1596. for i in "${!versions[@]}"; do
  1597. printf " ${C_GREEN}[%2d]${C_RESET} %s\n" "$((i+1))" "${versions[$i]}"
  1598. done
  1599. echo -e " ${C_RED} [ 0]${C_RESET} ↩️ Cancel"
  1600. local choice
  1601. while true; do
  1602. read -p "👉 Enter version number [1]: " choice
  1603. choice=${choice:-1}
  1604. if [[ "$choice" == "0" ]]; then return; fi
  1605. if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -le "${#versions[@]}" ]; then
  1606. SELECTED_VERSION="${versions[$((choice-1))]}"
  1607. break
  1608. else
  1609. echo -e "${C_RED}❌ Invalid selection.${C_RESET}"
  1610. fi
  1611. done
  1612. local ports
  1613. read -p "👉 Enter port(s) for Falcon Proxy (e.g., 8080 or 8080 8888) [8080]: " ports
  1614. ports=${ports:-8080}
  1615. local port_array=($ports)
  1616. for port in "${port_array[@]}"; do
  1617. if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
  1618. echo -e "\n${C_RED}❌ Invalid port number: $port. Aborting.${C_RESET}"
  1619. return
  1620. fi
  1621. check_and_free_ports "$port" || return
  1622. check_and_open_firewall_port "$port" tcp || return
  1623. done
  1624. echo -e "\n${C_GREEN}⚙️ Detecting system architecture...${C_RESET}"
  1625. local arch=$(uname -m)
  1626. local binary_name=""
  1627. if [[ "$arch" == "x86_64" ]]; then
  1628. binary_name="falconproxy"
  1629. echo -e "${C_BLUE}ℹ️ Detected x86_64 (amd64) architecture.${C_RESET}"
  1630. elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then
  1631. binary_name="falconproxyarm"
  1632. echo -e "${C_BLUE}ℹ️ Detected ARM64 architecture.${C_RESET}"
  1633. else
  1634. echo -e "\n${C_RED}❌ Unsupported architecture: $arch. Cannot install Falcon Proxy.${C_RESET}"
  1635. return
  1636. fi
  1637. # Construct download URL based on selected version
  1638. local download_url="https://github.com/firewallfalcons/FirewallFalcon-Manager/releases/download/$SELECTED_VERSION/$binary_name"
  1639. echo -e "\n${C_GREEN}📥 Downloading Falcon Proxy $SELECTED_VERSION ($binary_name)...${C_RESET}"
  1640. wget -q --show-progress -O "$FALCONPROXY_BINARY" "$download_url"
  1641. if [ $? -ne 0 ]; then
  1642. echo -e "\n${C_RED}❌ Failed to download the binary. Please ensure version $SELECTED_VERSION has asset '$binary_name'.${C_RESET}"
  1643. return
  1644. fi
  1645. chmod +x "$FALCONPROXY_BINARY"
  1646. echo -e "\n${C_GREEN}📝 Creating systemd service file...${C_RESET}"
  1647. cat > "$FALCONPROXY_SERVICE_FILE" <<EOF
  1648. [Unit]
  1649. Description=Falcon Proxy ($SELECTED_VERSION)
  1650. After=network.target
  1651. [Service]
  1652. User=root
  1653. Type=simple
  1654. ExecStart=$FALCONPROXY_BINARY -p $ports
  1655. Restart=always
  1656. RestartSec=2s
  1657. [Install]
  1658. WantedBy=default.target
  1659. EOF
  1660. echo -e "\n${C_GREEN}💾 Saving configuration...${C_RESET}"
  1661. cat > "$FALCONPROXY_CONFIG_FILE" <<EOF
  1662. PORTS="$ports"
  1663. INSTALLED_VERSION="$SELECTED_VERSION"
  1664. EOF
  1665. echo -e "\n${C_GREEN}▶️ Enabling and starting Falcon Proxy service...${C_RESET}"
  1666. systemctl daemon-reload
  1667. systemctl enable falconproxy.service
  1668. systemctl restart falconproxy.service
  1669. sleep 2
  1670. if systemctl is-active --quiet falconproxy; then
  1671. echo -e "\n${C_GREEN}✅ SUCCESS: Falcon Proxy $SELECTED_VERSION is installed and active.${C_RESET}"
  1672. echo -e " Listening on port(s): ${C_YELLOW}$ports${C_RESET}"
  1673. else
  1674. echo -e "\n${C_RED}❌ ERROR: Falcon Proxy service failed to start.${C_RESET}"
  1675. echo -e "${C_YELLOW}ℹ️ Displaying last 15 lines of the service log for diagnostics:${C_RESET}"
  1676. journalctl -u falconproxy.service -n 15 --no-pager
  1677. fi
  1678. }
  1679. uninstall_falcon_proxy() {
  1680. echo -e "\n${C_BOLD}${C_PURPLE}--- 🗑️ Uninstalling Falcon Proxy ---${C_RESET}"
  1681. if [ ! -f "$FALCONPROXY_SERVICE_FILE" ]; then
  1682. echo -e "${C_YELLOW}ℹ️ Falcon Proxy is not installed, skipping.${C_RESET}"
  1683. return
  1684. fi
  1685. echo -e "${C_GREEN}🛑 Stopping and disabling Falcon Proxy service...${C_RESET}"
  1686. systemctl stop falconproxy.service >/dev/null 2>&1
  1687. systemctl disable falconproxy.service >/dev/null 2>&1
  1688. echo -e "${C_GREEN}🗑️ Removing service file...${C_RESET}"
  1689. rm -f "$FALCONPROXY_SERVICE_FILE"
  1690. systemctl daemon-reload
  1691. echo -e "${C_GREEN}🗑️ Removing binary and config files...${C_RESET}"
  1692. rm -f "$FALCONPROXY_BINARY"
  1693. rm -f "$FALCONPROXY_CONFIG_FILE"
  1694. echo -e "${C_GREEN}✅ Falcon Proxy has been uninstalled successfully.${C_RESET}"
  1695. }
  1696. # --- ZiVPN Installation Logic ---
  1697. install_zivpn() {
  1698. clear; show_banner
  1699. echo -e "${C_BOLD}${C_PURPLE}--- 🚀 Installing ZiVPN (UDP/VPN) ---${C_RESET}"
  1700. if [ -f "$ZIVPN_SERVICE_FILE" ]; then
  1701. echo -e "\n${C_YELLOW}ℹ️ ZiVPN is already installed.${C_RESET}"
  1702. return
  1703. fi
  1704. echo -e "\n${C_GREEN}⚙️ Checking system architecture...${C_RESET}"
  1705. local arch=$(uname -m)
  1706. local zivpn_url=""
  1707. if [[ "$arch" == "x86_64" ]]; then
  1708. zivpn_url="https://github.com/zahidbd2/udp-zivpn/releases/download/udp-zivpn_1.4.9/udp-zivpn-linux-amd64"
  1709. echo -e "${C_BLUE}ℹ️ Detected AMD64/x86_64 architecture.${C_RESET}"
  1710. elif [[ "$arch" == "aarch64" ]]; then
  1711. zivpn_url="https://github.com/zahidbd2/udp-zivpn/releases/download/udp-zivpn_1.4.9/udp-zivpn-linux-arm64"
  1712. echo -e "${C_BLUE}ℹ️ Detected ARM64 architecture.${C_RESET}"
  1713. elif [[ "$arch" == "armv7l" || "$arch" == "arm" ]]; then
  1714. zivpn_url="https://github.com/zahidbd2/udp-zivpn/releases/download/udp-zivpn_1.4.9/udp-zivpn-linux-arm"
  1715. echo -e "${C_BLUE}ℹ️ Detected ARM architecture.${C_RESET}"
  1716. else
  1717. echo -e "${C_RED}❌ Unsupported architecture: $arch${C_RESET}"
  1718. return
  1719. fi
  1720. echo -e "\n${C_GREEN}📦 Downloading ZiVPN binary...${C_RESET}"
  1721. if ! wget -q --show-progress -O "$ZIVPN_BIN" "$zivpn_url"; then
  1722. echo -e "${C_RED}❌ Download failed. Check internet connection.${C_RESET}"
  1723. return
  1724. fi
  1725. chmod +x "$ZIVPN_BIN"
  1726. echo -e "\n${C_GREEN}⚙️ Configuring ZIVPN...${C_RESET}"
  1727. mkdir -p "$ZIVPN_DIR"
  1728. # Generate Certificates
  1729. echo -e "${C_BLUE}🔐 Generating self-signed certificates...${C_RESET}"
  1730. if ! command -v openssl &>/dev/null; then apt-get install -y openssl &>/dev/null; fi
  1731. openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
  1732. -subj "/C=US/ST=California/L=Los Angeles/O=Example Corp/OU=IT Department/CN=zivpn" \
  1733. -keyout "$ZIVPN_KEY_FILE" -out "$ZIVPN_CERT_FILE" 2>/dev/null
  1734. if [ ! -f "$ZIVPN_CERT_FILE" ]; then
  1735. echo -e "${C_RED}❌ Failed to generate certificates.${C_RESET}"
  1736. return
  1737. fi
  1738. # System Tuning
  1739. echo -e "${C_BLUE}🔧 Tuning system network parameters...${C_RESET}"
  1740. sysctl -w net.core.rmem_max=16777216 >/dev/null
  1741. sysctl -w net.core.wmem_max=16777216 >/dev/null
  1742. # Create Service
  1743. echo -e "${C_BLUE}📝 Creating systemd service file...${C_RESET}"
  1744. cat <<EOF > "$ZIVPN_SERVICE_FILE"
  1745. [Unit]
  1746. Description=zivpn VPN Server
  1747. After=network.target
  1748. [Service]
  1749. Type=simple
  1750. User=root
  1751. WorkingDirectory=$ZIVPN_DIR
  1752. ExecStart=$ZIVPN_BIN server -c $ZIVPN_CONFIG_FILE
  1753. Restart=always
  1754. RestartSec=3
  1755. Environment=ZIVPN_LOG_LEVEL=info
  1756. CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
  1757. AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
  1758. NoNewPrivileges=true
  1759. [Install]
  1760. WantedBy=multi-user.target
  1761. EOF
  1762. # Configure Passwords
  1763. echo -e "\n${C_YELLOW}🔑 ZiVPN Password Setup${C_RESET}"
  1764. read -p "👉 Enter passwords separated by commas (e.g., user1,user2) [Default: 'zi']: " input_config
  1765. if [ -n "$input_config" ]; then
  1766. IFS=',' read -r -a config_array <<< "$input_config"
  1767. # Ensure array format for JSON
  1768. json_passwords=$(printf '"%s",' "${config_array[@]}")
  1769. json_passwords="[${json_passwords%,}]"
  1770. else
  1771. json_passwords='["zi"]'
  1772. fi
  1773. # Create Config File
  1774. cat <<EOF > "$ZIVPN_CONFIG_FILE"
  1775. {
  1776. "listen": ":5667",
  1777. "cert": "$ZIVPN_CERT_FILE",
  1778. "key": "$ZIVPN_KEY_FILE",
  1779. "obfs":"zivpn",
  1780. "auth": {
  1781. "mode": "passwords",
  1782. "config": $json_passwords
  1783. }
  1784. }
  1785. EOF
  1786. echo -e "\n${C_GREEN}🚀 Starting ZiVPN Service...${C_RESET}"
  1787. systemctl daemon-reload
  1788. systemctl enable zivpn.service
  1789. systemctl start zivpn.service
  1790. # Port Forwarding / Firewall
  1791. echo -e "${C_BLUE}🔥 Configuring Firewall Rules (Redirecting 6000-19999 -> 5667)...${C_RESET}"
  1792. # Determine primary interface
  1793. local iface=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
  1794. if [ -n "$iface" ]; then
  1795. iptables -t nat -A PREROUTING -i "$iface" -p udp --dport 6000:19999 -j DNAT --to-destination :5667
  1796. # Note: IPTables rules are not persistent by default without iptables-persistent package
  1797. else
  1798. echo -e "${C_YELLOW}⚠️ Could not detect default interface for IPTables redirection.${C_RESET}"
  1799. fi
  1800. if command -v ufw &>/dev/null; then
  1801. ufw allow 6000:19999/udp >/dev/null
  1802. ufw allow 5667/udp >/dev/null
  1803. fi
  1804. # Cleanup
  1805. rm -f zi.sh zi2.sh 2>/dev/null
  1806. if systemctl is-active --quiet zivpn.service; then
  1807. echo -e "\n${C_GREEN}✅ ZiVPN Installed Successfully!${C_RESET}"
  1808. echo -e " - UDP Port: 5667 (Direct)"
  1809. echo -e " - UDP Ports: 6000-19999 (Forwarded)"
  1810. else
  1811. echo -e "\n${C_RED}❌ ZiVPN Service failed to start. Check logs: journalctl -u zivpn.service${C_RESET}"
  1812. fi
  1813. }
  1814. uninstall_zivpn() {
  1815. clear; show_banner
  1816. echo -e "${C_BOLD}${C_PURPLE}--- 🗑️ Uninstall ZiVPN ---${C_RESET}"
  1817. if [ ! -f "$ZIVPN_SERVICE_FILE" ] && [ ! -f "$ZIVPN_BIN" ]; then
  1818. echo -e "\n${C_YELLOW}ℹ️ ZiVPN does not appear to be installed.${C_RESET}"
  1819. return
  1820. fi
  1821. read -p "👉 Are you sure you want to uninstall ZiVPN? (y/n): " confirm
  1822. if [[ "$confirm" != "y" ]]; then echo -e "${C_YELLOW}Cancelled.${C_RESET}"; return; fi
  1823. echo -e "\n${C_BLUE}🛑 Stopping services...${C_RESET}"
  1824. systemctl stop zivpn.service 2>/dev/null
  1825. systemctl disable zivpn.service 2>/dev/null
  1826. echo -e "${C_BLUE}🗑️ Removing files...${C_RESET}"
  1827. rm -f "$ZIVPN_SERVICE_FILE"
  1828. rm -rf "$ZIVPN_DIR"
  1829. rm -f "$ZIVPN_BIN"
  1830. systemctl daemon-reload
  1831. # Clean cache (from original uninstall script logic)
  1832. echo -e "${C_BLUE}🧹 Cleaning memory cache...${C_RESET}"
  1833. sync; echo 3 > /proc/sys/vm/drop_caches
  1834. echo -e "\n${C_GREEN}✅ ZiVPN Uninstalled Successfully.${C_RESET}"
  1835. }
  1836. purge_nginx() {
  1837. local mode="$1"
  1838. if [[ "$mode" != "silent" ]]; then
  1839. clear; show_banner
  1840. echo -e "${C_BOLD}${C_PURPLE}--- 🔥 Purge Internal Nginx Installation ---${C_RESET}"
  1841. if ! command -v nginx &> /dev/null; then
  1842. echo -e "\n${C_YELLOW}ℹ️ Nginx is not installed. Nothing to do.${C_RESET}"
  1843. return
  1844. fi
  1845. read -p "👉 This will COMPLETELY REMOVE Nginx and all its configuration files. Are you sure? (y/n): " confirm
  1846. if [[ "$confirm" != "y" ]]; then
  1847. echo -e "\n${C_YELLOW}❌ Uninstallation cancelled.${C_RESET}"
  1848. return
  1849. fi
  1850. fi
  1851. echo -e "\n${C_BLUE}🛑 Stopping Nginx service...${C_RESET}"
  1852. systemctl stop nginx >/dev/null 2>&1
  1853. echo -e "\n${C_BLUE}🗑️ Purging Nginx packages...${C_RESET}"
  1854. apt-get purge -y nginx nginx-common >/dev/null 2>&1
  1855. apt-get autoremove -y >/dev/null 2>&1
  1856. echo -e "\n${C_BLUE}🗑️ Removing leftover files...${C_RESET}"
  1857. rm -f /etc/ssl/certs/nginx-selfsigned.pem
  1858. rm -f /etc/ssl/private/nginx-selfsigned.key
  1859. rm -rf /etc/nginx
  1860. rm -f "${NGINX_CONFIG_FILE}.bak"
  1861. rm -f "${NGINX_CONFIG_FILE}.bak.certbot"
  1862. rm -f "${NGINX_CONFIG_FILE}.bak.selfsigned"
  1863. if [[ "$mode" != "silent" ]]; then
  1864. echo -e "\n${C_GREEN}✅ Nginx has been completely purged from the system.${C_RESET}"
  1865. fi
  1866. }
  1867. install_nginx_proxy() {
  1868. clear; show_banner
  1869. echo -e "${C_BOLD}${C_PURPLE}--- 🚀 Installing Internal Nginx Proxy ---${C_RESET}"
  1870. echo -e "${C_DIM}This acts as the internal traffic handler for HAProxy on ports 8443 and 8880.${C_RESET}"
  1871. if command -v nginx &> /dev/null; then
  1872. echo -e "\n${C_YELLOW}⚠️ An existing Nginx installation was found.${C_RESET}"
  1873. read -p "👉 To ensure a clean setup, the existing Nginx will be purged. Continue? (y/n): " confirm_purge
  1874. if [[ "$confirm_purge" != "y" ]]; then
  1875. echo -e "\n${C_RED}❌ Installation cancelled.${C_RESET}"
  1876. return
  1877. fi
  1878. purge_nginx "silent"
  1879. fi
  1880. echo -e "\n${C_BLUE}📦 Installing Nginx package...${C_RESET}"
  1881. apt-get update && apt-get install -y nginx || { echo -e "${C_RED}❌ Failed to install Nginx.${C_RESET}"; return; }
  1882. # By default matching HAProxy configuration routing rules
  1883. local tls_ports
  1884. read -p "👉 Enter internal TLS/SSL Port(s) [Default: 8443]: " input_tls
  1885. if [[ -z "$input_tls" ]]; then tls_ports="8443"; else tls_ports="$input_tls"; fi
  1886. local http_ports
  1887. read -p "👉 Enter internal HTTP/Non-TLS Port(s) [Default: 8880]: " input_http
  1888. if [[ -z "$input_http" ]]; then http_ports="8880"; else http_ports="$input_http"; fi
  1889. # Convert to arrays
  1890. read -a tls_ports_array <<< "$tls_ports"
  1891. read -a http_ports_array <<< "$http_ports"
  1892. # Process Ports: Free and Open
  1893. for port in "${tls_ports_array[@]}" "${http_ports_array[@]}"; do
  1894. if ! [[ "$port" =~ ^[0-9]+$ ]]; then echo -e "${C_RED}❌ Invalid port: $port${C_RESET}"; return; fi
  1895. check_and_free_ports "$port" || return
  1896. check_and_open_firewall_port "$port" tcp || return
  1897. done
  1898. echo -e "\n${C_GREEN}🔐 Generating self-signed SSL certificate for internal Nginx...${C_RESET}"
  1899. local SSL_CERT="/etc/ssl/certs/nginx-selfsigned.pem"
  1900. local SSL_KEY="/etc/ssl/private/nginx-selfsigned.key"
  1901. mkdir -p /etc/ssl/certs /etc/ssl/private
  1902. openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  1903. -keyout "$SSL_KEY" \
  1904. -out "$SSL_CERT" \
  1905. -subj "/CN=firewallfalcon.proxy" >/dev/null 2>&1 || { echo -e "${C_RED}❌ Failed to generate SSL certificate.${C_RESET}"; return; }
  1906. echo -e "\n${C_GREEN}📝 Applying Nginx reverse proxy configuration...${C_RESET}"
  1907. mv "$NGINX_CONFIG_FILE" "${NGINX_CONFIG_FILE}.bak" 2>/dev/null
  1908. # --- Generate Listen Directives ---
  1909. local listen_block=""
  1910. for port in "${http_ports_array[@]}"; do
  1911. listen_block="${listen_block} listen $port;\n listen [::]:$port;\n"
  1912. done
  1913. for port in "${tls_ports_array[@]}"; do
  1914. listen_block="${listen_block} listen $port ssl http2;\n listen [::]:$port ssl http2;\n"
  1915. done
  1916. cat > "$NGINX_CONFIG_FILE" <<EOF
  1917. server {
  1918. server_tokens off;
  1919. server_name _;
  1920. $(echo -e "$listen_block")
  1921. ssl_certificate /etc/ssl/certs/nginx-selfsigned.pem;
  1922. ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
  1923. ssl_protocols TLSv1.2 TLSv1.3;
  1924. ssl_ciphers HIGH:!aNULL:!eNULL:!MD5:!DES:!RC4:!ADH!SSLv3:!EXP!PSK!DSS;
  1925. resolver 8.8.8.8;
  1926. location ~ ^/(?<fwdport>\d+)/(?<fwdpath>.*)$ {
  1927. client_max_body_size 0;
  1928. client_body_timeout 1d;
  1929. grpc_read_timeout 1d;
  1930. grpc_socket_keepalive on;
  1931. proxy_read_timeout 1d;
  1932. proxy_http_version 1.1;
  1933. proxy_buffering off;
  1934. proxy_request_buffering off;
  1935. proxy_socket_keepalive on;
  1936. proxy_set_header Upgrade \$http_upgrade;
  1937. proxy_set_header Connection "upgrade";
  1938. proxy_set_header Host \$host;
  1939. proxy_set_header X-Real-IP \$remote_addr;
  1940. proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  1941. if (\$content_type ~* "GRPC") { grpc_pass grpc://127.0.0.1:\$fwdport\$is_args\$args; break; }
  1942. proxy_pass http://127.0.0.1:\$fwdport\$is_args\$args;
  1943. break;
  1944. }
  1945. location / {
  1946. proxy_read_timeout 3600s;
  1947. proxy_buffering off;
  1948. proxy_request_buffering off;
  1949. proxy_http_version 1.1;
  1950. proxy_socket_keepalive on;
  1951. tcp_nodelay on;
  1952. tcp_nopush off;
  1953. proxy_pass http://127.0.0.1:8080;
  1954. proxy_set_header Upgrade \$http_upgrade;
  1955. proxy_set_header Connection "upgrade";
  1956. proxy_set_header Host \$host;
  1957. proxy_set_header X-Real-IP \$remote_addr;
  1958. proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  1959. }
  1960. }
  1961. EOF
  1962. echo -e "\n${C_GREEN}▶️ Restarting Nginx service...${C_RESET}"
  1963. systemctl restart nginx
  1964. sleep 2
  1965. if systemctl is-active --quiet nginx; then
  1966. echo -e "\n${C_GREEN}✅ SUCCESS: Internal Nginx Reverse Proxy is active.${C_RESET}"
  1967. echo -e " - Internal TLS Ports: ${C_YELLOW}${tls_ports}${C_RESET}"
  1968. echo -e " - Internal HTTP Ports: ${C_YELLOW}${http_ports}${C_RESET}"
  1969. # Save ports for future reference
  1970. echo "TLS_PORTS=\"$tls_ports\"" > "$NGINX_PORTS_FILE"
  1971. echo "HTTP_PORTS=\"$http_ports\"" >> "$NGINX_PORTS_FILE"
  1972. else
  1973. echo -e "\n${C_RED}❌ ERROR: Nginx service failed to start.${C_RESET}"
  1974. echo -e "${C_YELLOW}ℹ️ Displaying Nginx status for diagnostics:${C_RESET}"
  1975. systemctl status nginx --no-pager
  1976. echo -e "${C_YELLOW}🔄 Restoring previous Nginx config...${C_RESET}"
  1977. mv "${NGINX_CONFIG_FILE}.bak" "$NGINX_CONFIG_FILE" 2>/dev/null
  1978. fi
  1979. }
  1980. request_certbot_ssl() {
  1981. clear; show_banner
  1982. echo -e "${C_BOLD}${C_PURPLE}--- 🔒 Request Let's Encrypt SSL for HAProxy ---${C_RESET}"
  1983. if ! systemctl is-active --quiet haproxy; then
  1984. echo -e "\n${C_RED}❌ HAProxy is not running. Please ensure HAProxy is installed and active on Port 80.${C_RESET}"
  1985. return
  1986. fi
  1987. _install_certbot || return
  1988. echo
  1989. read -p "👉 Enter your domain name (e.g., vps.example.com): " domain_name
  1990. if [[ -z "$domain_name" ]]; then
  1991. echo -e "\n${C_RED}❌ Domain name cannot be empty. Aborting.${C_RESET}"
  1992. return
  1993. fi
  1994. read -p "👉 Enter your email address (for Let's Encrypt): " email
  1995. if [[ -z "$email" ]]; then
  1996. echo -e "\n${C_RED}❌ Email address cannot be empty. Aborting.${C_RESET}"
  1997. return
  1998. fi
  1999. echo -e "\n${C_BLUE}🛑 Stopping Proxies temporarily for validation on Port 80...${C_RESET}"
  2000. systemctl stop haproxy >/dev/null 2>&1
  2001. systemctl stop nginx >/dev/null 2>&1
  2002. sleep 2
  2003. if ss -lntp | grep -q ":80\s"; then
  2004. echo -e "${C_RED}❌ Failed to free port 80, another process might be using it. Aborting.${C_RESET}"
  2005. systemctl start haproxy >/dev/null 2>&1
  2006. systemctl start nginx >/dev/null 2>&1
  2007. return
  2008. fi
  2009. echo -e "\n${C_BLUE}🚀 Requesting certificate for ${C_YELLOW}$domain_name...${C_RESET}"
  2010. certbot certonly --standalone -d "$domain_name" --non-interactive --agree-tos -m "$email"
  2011. if [ $? -ne 0 ]; then
  2012. echo -e "\n${C_RED}❌ Certbot failed to obtain a certificate.${C_RESET}"
  2013. echo -e "${C_YELLOW}ℹ️ Please check your domain's DNS 'A' record points to this server's IP.${C_RESET}"
  2014. systemctl start haproxy >/dev/null 2>&1
  2015. systemctl start nginx >/dev/null 2>&1
  2016. return
  2017. fi
  2018. local SSL_CERT_LIVE="/etc/letsencrypt/live/$domain_name/fullchain.pem"
  2019. local SSL_KEY_LIVE="/etc/letsencrypt/live/$domain_name/privkey.pem"
  2020. if [ ! -f "$SSL_CERT_LIVE" ] || [ ! -f "$SSL_KEY_LIVE" ]; then
  2021. echo -e "\n${C_RED}❌ Certbot succeeded, but cert files not found at expected location.${C_RESET}"
  2022. systemctl start haproxy >/dev/null 2>&1
  2023. systemctl start nginx >/dev/null 2>&1
  2024. return
  2025. fi
  2026. echo -e "\n${C_GREEN}✅ Certificate obtained successfully!${C_RESET}"
  2027. echo -e "${C_BLUE}📝 Updating HAProxy certificate...${C_RESET}"
  2028. mkdir -p "$SSL_CERT_DIR"
  2029. cat "$SSL_CERT_LIVE" "$SSL_KEY_LIVE" > "$SSL_CERT_FILE"
  2030. echo -e "\n${C_BLUE}▶️ Restarting Services with new certificate...${C_RESET}"
  2031. systemctl start haproxy >/dev/null 2>&1
  2032. systemctl start nginx >/dev/null 2>&1
  2033. sleep 2
  2034. if systemctl is-active --quiet haproxy; then
  2035. echo -e "\n${C_GREEN}✅ SUCCESS: HAProxy is active with your new Let's Encrypt certificate!${C_RESET}"
  2036. else
  2037. echo -e "\n${C_RED}❌ ERROR: HAProxy failed to start with the new certificate.${C_RESET}"
  2038. fi
  2039. }
  2040. nginx_proxy_menu() {
  2041. clear; show_banner
  2042. echo -e "${C_BOLD}${C_PURPLE}--- 🌐 Internal Web Proxy Management ---${C_RESET}"
  2043. local active_status="${C_STATUS_I}Inactive${C_RESET}"
  2044. if systemctl is-active --quiet nginx; then
  2045. active_status="${C_STATUS_A}Active${C_RESET}"
  2046. fi
  2047. # Retrieve Ports Info
  2048. local ports_info=""
  2049. if [ -f "$NGINX_PORTS_FILE" ]; then
  2050. source "$NGINX_PORTS_FILE"
  2051. ports_info="\n ${C_DIM}Internal TLS: $TLS_PORTS | Internal HTTP: $HTTP_PORTS${C_RESET}"
  2052. fi
  2053. echo -e "\n${C_WHITE}Current Nginx Status: ${active_status}${ports_info}"
  2054. echo -e "\n${C_BOLD}Select an action:${C_RESET}\n"
  2055. if systemctl is-active --quiet nginx; then
  2056. printf " ${C_CHOICE}[ 1]${C_RESET} %-40s\n" "🛑 Stop Internal Nginx Service"
  2057. printf " ${C_CHOICE}[ 2]${C_RESET} %-40s\n" "🔄 Restart Internal Nginx Service"
  2058. printf " ${C_CHOICE}[ 3]${C_RESET} %-40s\n" "⚙️ Re-install/Re-configure (Change Ports)"
  2059. printf " ${C_CHOICE}[ 4]${C_RESET} %-40s\n" "🔒 Request/Renew HAProxy SSL (Certbot)"
  2060. printf " ${C_CHOICE}[ 5]${C_RESET} %-40s\n" "🔥 Uninstall/Purge Internal Nginx"
  2061. else
  2062. printf " ${C_CHOICE}[ 1]${C_RESET} %-40s\n" "▶️ Start Internal Nginx Service"
  2063. printf " ${C_CHOICE}[ 3]${C_RESET} %-40s\n" "⚙️ Install/Configure Internal Nginx"
  2064. printf " ${C_CHOICE}[ 5]${C_RESET} %-40s\n" "🔥 Uninstall/Purge Internal Nginx"
  2065. fi
  2066. echo -e "\n ${C_WARN}[ 0]${C_RESET} ↩️ Return to previous menu"
  2067. echo
  2068. read -p "👉 Enter your choice: " choice
  2069. case $choice in
  2070. 1)
  2071. if systemctl is-active --quiet nginx; then
  2072. echo -e "\n${C_BLUE}🛑 Stopping Nginx...${C_RESET}"
  2073. systemctl stop nginx
  2074. echo -e "${C_GREEN}✅ Nginx stopped.${C_RESET}"
  2075. else
  2076. echo -e "\n${C_BLUE}▶️ Starting Nginx...${C_RESET}"
  2077. systemctl start nginx
  2078. if systemctl is-active --quiet nginx; then echo -e "${C_GREEN}✅ Nginx Started.${C_RESET}"; else echo -e "${C_RED}❌ Failed to start.${C_RESET}"; fi
  2079. fi
  2080. press_enter
  2081. ;;
  2082. 2)
  2083. echo -e "\n${C_BLUE}🔄 Restarting Nginx...${C_RESET}"
  2084. systemctl restart nginx
  2085. press_enter
  2086. ;;
  2087. 3)
  2088. install_nginx_proxy; press_enter
  2089. ;;
  2090. 4)
  2091. request_certbot_ssl; press_enter
  2092. ;;
  2093. 5)
  2094. purge_nginx; press_enter
  2095. ;;
  2096. 0) return ;;
  2097. *) invalid_option ;;
  2098. esac
  2099. }
  2100. install_xui_panel() {
  2101. clear; show_banner
  2102. echo -e "${C_BOLD}${C_PURPLE}--- 🚀 Install X-UI Panel ---${C_RESET}"
  2103. echo -e "\nThis will download and run the official installation script for X-UI."
  2104. echo -e "Choose an installation option:\n"
  2105. printf " ${C_GREEN}[ 1]${C_RESET} %-40s\n" "Install the latest version of X-UI"
  2106. printf " ${C_GREEN}[ 2]${C_RESET} %-40s\n" "Install a specific version of X-UI"
  2107. echo -e "\n ${C_RED}[ 0]${C_RESET} ❌ Cancel Installation"
  2108. echo
  2109. read -p "👉 Select an option: " choice
  2110. case $choice in
  2111. 1)
  2112. echo -e "\n${C_BLUE}⚙️ Installing the latest version...${C_RESET}"
  2113. bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh)
  2114. ;;
  2115. 2)
  2116. read -p "👉 Enter the version to install (e.g., 1.8.0): " version
  2117. if [[ -z "$version" ]]; then
  2118. echo -e "\n${C_RED}❌ Version number cannot be empty.${C_RESET}"
  2119. return
  2120. fi
  2121. echo -e "\n${C_BLUE}⚙️ Installing version ${C_YELLOW}$version...${C_RESET}"
  2122. VERSION=$version bash <(curl -Ls "https://raw.githubusercontent.com/alireza0/x-ui/$version/install.sh") "$version"
  2123. ;;
  2124. 0)
  2125. echo -e "\n${C_YELLOW}❌ Installation cancelled.${C_RESET}"
  2126. ;;
  2127. *)
  2128. echo -e "\n${C_RED}❌ Invalid option.${C_RESET}"
  2129. ;;
  2130. esac
  2131. }
  2132. uninstall_xui_panel() {
  2133. clear; show_banner
  2134. echo -e "${C_BOLD}${C_PURPLE}--- 🗑️ Uninstall X-UI Panel ---${C_RESET}"
  2135. if ! command -v x-ui &> /dev/null; then
  2136. echo -e "\n${C_YELLOW}ℹ️ X-UI does not appear to be installed.${C_RESET}"
  2137. return
  2138. fi
  2139. read -p "👉 Are you sure you want to thoroughly uninstall X-UI? (y/n): " confirm
  2140. if [[ "$confirm" == "y" ]]; then
  2141. echo -e "\n${C_BLUE}⚙️ Running the default X-UI uninstaller first...${C_RESET}"
  2142. x-ui uninstall >/dev/null 2>&1
  2143. echo -e "\n${C_BLUE}🧹 Performing a full cleanup to ensure complete removal...${C_RESET}"
  2144. echo " - Stopping and disabling x-ui service..."
  2145. systemctl stop x-ui >/dev/null 2>&1
  2146. systemctl disable x-ui >/dev/null 2>&1
  2147. echo " - Removing x-ui files and directories..."
  2148. rm -f /etc/systemd/system/x-ui.service
  2149. rm -f /usr/local/bin/x-ui
  2150. rm -rf /usr/local/x-ui/
  2151. rm -rf /etc/x-ui/
  2152. echo " - Reloading systemd daemon..."
  2153. systemctl daemon-reload
  2154. echo -e "\n${C_GREEN}✅ X-UI has been thoroughly uninstalled.${C_RESET}"
  2155. else
  2156. echo -e "\n${C_YELLOW}❌ Uninstallation cancelled.${C_RESET}"
  2157. fi
  2158. }
  2159. show_banner() {
  2160. local os_name=$(grep -oP 'PRETTY_NAME="\K[^"]+' /etc/os-release || echo "Linux")
  2161. local up_time=$(uptime -p | sed 's/up //')
  2162. local ram_usage=$(free -m | awk '/^Mem:/{printf "%.2f", $3*100/$2}')
  2163. # Efficient CPU Load check (Load Average)
  2164. local cpu_load=$(cat /proc/loadavg | awk '{print $1}')
  2165. local online_users=0
  2166. # Optimize online user count: Get total active sshd procs roughly (may overcount if multiple procs per session but faster)
  2167. # Or just keep it if DB is small. Let's trust pgrep is okay for menu load.
  2168. if [[ -s "$DB_FILE" ]]; then
  2169. while IFS=: read -r user pass expiry limit; do
  2170. # Use pgrep -c for speed
  2171. local count=$(pgrep -c -u "$user" sshd)
  2172. online_users=$((online_users + count))
  2173. done < "$DB_FILE"
  2174. fi
  2175. local total_users=0
  2176. if [[ -s "$DB_FILE" ]]; then total_users=$(grep -c . "$DB_FILE"); fi
  2177. clear
  2178. echo
  2179. echo -e "${C_TITLE} FirewallFalcon Manager ${C_RESET}${C_DIM}| v4.0.0 Premium Edition${C_RESET}"
  2180. echo -e "${C_BLUE} ─────────────────────────────────────────────────────────${C_RESET}"
  2181. printf " ${C_GRAY}%-10s${C_RESET} %-20s ${C_GRAY}|${C_RESET} %s\n" "OS" "$os_name" "Uptime: $up_time"
  2182. 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}"
  2183. printf " ${C_GRAY}%-10s${C_RESET} %-20s ${C_GRAY}|${C_RESET} %s\n" "Users" "${total_users} Managed Accounts" "Sys Load (1m): ${C_GREEN}${cpu_load}${C_RESET}"
  2184. echo -e "${C_BLUE} ─────────────────────────────────────────────────────────${C_RESET}"
  2185. }
  2186. protocol_menu() {
  2187. while true; do
  2188. show_banner
  2189. local badvpn_status; if systemctl is-active --quiet badvpn; then badvpn_status="${C_STATUS_A}(Active)${C_RESET}"; else badvpn_status="${C_STATUS_I}(Inactive)${C_RESET}"; fi
  2190. local udp_custom_status; if systemctl is-active --quiet udp-custom; then udp_custom_status="${C_STATUS_A}(Active)${C_RESET}"; else udp_custom_status="${C_STATUS_I}(Inactive)${C_RESET}"; fi
  2191. local zivpn_status; if systemctl is-active --quiet zivpn.service; then zivpn_status="${C_STATUS_A}(Active)${C_RESET}"; else zivpn_status="${C_STATUS_I}(Inactive)${C_RESET}"; fi
  2192. local ssl_tunnel_status="${C_STATUS_I}(Inactive)${C_RESET}"
  2193. if systemctl is-active --quiet haproxy; then
  2194. ssl_tunnel_status="${C_STATUS_A}(Active)${C_RESET}"
  2195. fi
  2196. local dnstt_status; if systemctl is-active --quiet dnstt.service; then dnstt_status="${C_STATUS_A}(Active)${C_RESET}"; else dnstt_status="${C_STATUS_I}(Inactive)${C_RESET}"; fi
  2197. local falconproxy_status="${C_STATUS_I}(Inactive)${C_RESET}"
  2198. local falconproxy_ports=""
  2199. if systemctl is-active --quiet falconproxy; then
  2200. if [ -f "$FALCONPROXY_CONFIG_FILE" ]; then source "$FALCONPROXY_CONFIG_FILE"; fi
  2201. falconproxy_ports=" ($PORTS)"
  2202. falconproxy_status="${C_STATUS_A}(Active - ${INSTALLED_VERSION:-latest})${C_RESET}"
  2203. fi
  2204. local nginx_status; if systemctl is-active --quiet nginx; then nginx_status="${C_STATUS_A}(Active)${C_RESET}"; else nginx_status="${C_STATUS_I}(Inactive)${C_RESET}"; fi
  2205. local xui_status; if command -v x-ui &> /dev/null; then xui_status="${C_STATUS_A}(Installed)${C_RESET}"; else xui_status="${C_STATUS_I}(Not Installed)${C_RESET}"; fi
  2206. echo -e "\n ${C_TITLE}══════════════[ ${C_BOLD}🔌 PROTOCOL & PANEL MANAGEMENT ${C_RESET}${C_TITLE}]══════════════${C_RESET}"
  2207. echo -e " ${C_ACCENT}--- TUNNELLING PROTOCOLS---${C_RESET}"
  2208. printf " ${C_CHOICE}[ 1]${C_RESET} %-45s %s\n" "🚀 Install badvpn (UDP 7300)" "$badvpn_status"
  2209. printf " ${C_CHOICE}[ 2]${C_RESET} %-45s\n" "🗑️ Uninstall badvpn"
  2210. printf " ${C_CHOICE}[ 3]${C_RESET} %-45s %s\n" "🚀 Install udp-custom" "$udp_custom_status"
  2211. printf " ${C_CHOICE}[ 4]${C_RESET} %-45s\n" "🗑️ Uninstall udp-custom"
  2212. printf " ${C_CHOICE}[ 5]${C_RESET} %-45s %s\n" "🔒 Install Multiplexed HAProxy (80/443)" "$ssl_tunnel_status"
  2213. printf " ${C_CHOICE}[ 6]${C_RESET} %-45s\n" "🗑️ Uninstall HAProxy"
  2214. printf " ${C_CHOICE}[ 7]${C_RESET} %-45s %s\n" "📡 Install/View DNSTT (Port 53)" "$dnstt_status"
  2215. printf " ${C_CHOICE}[ 8]${C_RESET} %-45s\n" "🗑️ Uninstall DNSTT"
  2216. printf " ${C_CHOICE}[ 9]${C_RESET} %-45s %s\n" "🦅 Install Falcon Proxy (Select Version)" "$falconproxy_status"
  2217. printf " ${C_CHOICE}[10]${C_RESET} %-45s\n" "🗑️ Uninstall Falcon Proxy"
  2218. printf " ${C_CHOICE}[11]${C_RESET} %-45s %s\n" "🌐 Install/Manage Internal Nginx" "$nginx_status"
  2219. printf " ${C_CHOICE}[16]${C_RESET} %-45s %s\n" "🛡️ Install ZiVPN (UDP 5667)" "$zivpn_status"
  2220. printf " ${C_CHOICE}[17]${C_RESET} %-45s\n" "🗑️ Uninstall ZiVPN"
  2221. echo -e " ${C_ACCENT}--- 💻 MANAGEMENT PANELS ---${C_RESET}"
  2222. printf " ${C_CHOICE}[12]${C_RESET} %-45s %s\n" "💻 Install X-UI Panel" "$xui_status"
  2223. printf " ${C_CHOICE}[13]${C_RESET} %-45s\n" "🗑️ Uninstall X-UI Panel"
  2224. echo -e " ${C_DIM}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~${C_RESET}"
  2225. echo -e " ${C_WARN}[ 0]${C_RESET} ↩️ Return to Main Menu"
  2226. echo
  2227. read -p "$(echo -e ${C_PROMPT}"👉 Select an option: "${C_RESET})" choice
  2228. case $choice in
  2229. 1) install_badvpn; press_enter ;; 2) uninstall_badvpn; press_enter ;;
  2230. 3) install_udp_custom; press_enter ;; 4) uninstall_udp_custom; press_enter ;;
  2231. 5) install_ssl_tunnel; press_enter ;; 6) uninstall_ssl_tunnel; press_enter ;;
  2232. 7) install_dnstt; press_enter ;; 8) uninstall_dnstt; press_enter ;;
  2233. 9) install_falcon_proxy; press_enter ;; 10) uninstall_falcon_proxy; press_enter ;;
  2234. 11) nginx_proxy_menu ;;
  2235. 12) install_xui_panel; press_enter ;; 13) uninstall_xui_panel; press_enter ;;
  2236. 16) install_zivpn; press_enter ;; 17) uninstall_zivpn; press_enter ;;
  2237. 0) return ;;
  2238. *) invalid_option ;;
  2239. esac
  2240. done
  2241. }
  2242. uninstall_script() {
  2243. clear; show_banner
  2244. echo -e "${C_RED}=====================================================${C_RESET}"
  2245. echo -e "${C_RED} 🔥 DANGER: UNINSTALL SCRIPT & ALL DATA 🔥 ${C_RESET}"
  2246. echo -e "${C_RED}=====================================================${C_RESET}"
  2247. echo -e "${C_YELLOW}This will PERMANENTLY remove this script and all its components, including:"
  2248. echo -e " - The main command ($(command -v menu))"
  2249. echo -e " - All configuration and user data ($DB_DIR)"
  2250. echo -e " - The active limiter service ($LIMITER_SERVICE)"
  2251. echo -e " - All installed services (badvpn, udp-custom, SSL Tunnel, Nginx, DNSTT)"
  2252. echo -e "\n${C_RED}This action is irreversible.${C_RESET}"
  2253. echo ""
  2254. read -p "👉 Type 'yes' to confirm and proceed with uninstallation: " confirm
  2255. if [[ "$confirm" != "yes" ]]; then
  2256. echo -e "\n${C_GREEN}✅ Uninstallation cancelled.${C_RESET}"
  2257. return
  2258. fi
  2259. export UNINSTALL_MODE="silent"
  2260. echo -e "\n${C_BLUE}--- 💥 Starting Uninstallation 💥 ---${C_RESET}"
  2261. echo -e "\n${C_BLUE}🗑️ Removing active limiter service...${C_RESET}"
  2262. systemctl stop firewallfalcon-limiter &>/dev/null
  2263. systemctl disable firewallfalcon-limiter &>/dev/null
  2264. rm -f "$LIMITER_SERVICE"
  2265. rm -f "$LIMITER_SCRIPT"
  2266. echo -e "\n${C_BLUE}🗑️ Removing bandwidth monitoring service...${C_RESET}"
  2267. systemctl stop firewallfalcon-bandwidth &>/dev/null
  2268. systemctl disable firewallfalcon-bandwidth &>/dev/null
  2269. rm -f "$BANDWIDTH_SERVICE"
  2270. rm -f "$BANDWIDTH_SCRIPT"
  2271. rm -f "$TRIAL_CLEANUP_SCRIPT"
  2272. echo -e "\n${C_BLUE}\ud83d\uddd1\ufe0f Removing SSH login banner...${C_RESET}"
  2273. rm -f "$LOGIN_INFO_SCRIPT"
  2274. rm -f "$SSHD_FF_CONFIG"
  2275. systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null
  2276. chattr -i /etc/resolv.conf &>/dev/null
  2277. purge_nginx "silent"
  2278. uninstall_dnstt
  2279. uninstall_badvpn
  2280. uninstall_udp_custom
  2281. uninstall_ssl_tunnel
  2282. uninstall_falcon_proxy
  2283. uninstall_zivpn
  2284. delete_dns_record
  2285. echo -e "\n${C_BLUE}🔄 Reloading systemd daemon...${C_RESET}"
  2286. systemctl daemon-reload
  2287. echo -e "\n${C_BLUE}🗑️ Removing script and configuration files...${C_RESET}"
  2288. rm -rf "$BADVPN_BUILD_DIR"
  2289. rm -rf "$UDP_CUSTOM_DIR"
  2290. rm -rf "$DB_DIR"
  2291. rm -f "$(command -v menu)"
  2292. echo -e "\n${C_GREEN}=============================================${C_RESET}"
  2293. echo -e "${C_GREEN} Script has been successfully uninstalled. ${C_RESET}"
  2294. echo -e "${C_GREEN}=============================================${C_RESET}"
  2295. echo -e "\nAll associated files and services have been removed."
  2296. echo "The 'menu' command will no longer work."
  2297. exit 0
  2298. }
  2299. # --- NEW FEATURES ---
  2300. create_trial_account() {
  2301. clear; show_banner
  2302. echo -e "${C_BOLD}${C_PURPLE}--- ⏱️ Create Trial/Test Account ---${C_RESET}"
  2303. # Ensure 'at' daemon is available
  2304. if ! command -v at &>/dev/null; then
  2305. echo -e "${C_YELLOW}⚠️ 'at' command not found. Installing...${C_RESET}"
  2306. apt-get update > /dev/null 2>&1 && apt-get install -y at || {
  2307. echo -e "${C_RED}❌ Failed to install 'at'. Cannot schedule auto-expiry.${C_RESET}"
  2308. return
  2309. }
  2310. systemctl enable atd &>/dev/null
  2311. systemctl start atd &>/dev/null
  2312. fi
  2313. # Ensure atd is running
  2314. if ! systemctl is-active --quiet atd; then
  2315. systemctl start atd &>/dev/null
  2316. fi
  2317. echo -e "\n${C_CYAN}Select trial duration:${C_RESET}\n"
  2318. printf " ${C_GREEN}[ 1]${C_RESET} ⏱️ 1 Hour\n"
  2319. printf " ${C_GREEN}[ 2]${C_RESET} ⏱️ 2 Hours\n"
  2320. printf " ${C_GREEN}[ 3]${C_RESET} ⏱️ 3 Hours\n"
  2321. printf " ${C_GREEN}[ 4]${C_RESET} ⏱️ 6 Hours\n"
  2322. printf " ${C_GREEN}[ 5]${C_RESET} ⏱️ 12 Hours\n"
  2323. printf " ${C_GREEN}[ 6]${C_RESET} 📅 1 Day\n"
  2324. printf " ${C_GREEN}[ 7]${C_RESET} 📅 3 Days\n"
  2325. printf " ${C_GREEN}[ 8]${C_RESET} ⚙️ Custom (enter hours)\n"
  2326. echo -e "\n ${C_RED}[ 0]${C_RESET} ↩️ Cancel"
  2327. echo
  2328. read -p "👉 Select duration: " dur_choice
  2329. local duration_hours=0
  2330. local duration_label=""
  2331. case $dur_choice in
  2332. 1) duration_hours=1; duration_label="1 Hour" ;;
  2333. 2) duration_hours=2; duration_label="2 Hours" ;;
  2334. 3) duration_hours=3; duration_label="3 Hours" ;;
  2335. 4) duration_hours=6; duration_label="6 Hours" ;;
  2336. 5) duration_hours=12; duration_label="12 Hours" ;;
  2337. 6) duration_hours=24; duration_label="1 Day" ;;
  2338. 7) duration_hours=72; duration_label="3 Days" ;;
  2339. 8) read -p "👉 Enter custom duration in hours: " custom_hours
  2340. if ! [[ "$custom_hours" =~ ^[0-9]+$ ]] || [[ "$custom_hours" -lt 1 ]]; then
  2341. echo -e "\n${C_RED}❌ Invalid number of hours.${C_RESET}"; return
  2342. fi
  2343. duration_hours=$custom_hours
  2344. duration_label="$custom_hours Hours"
  2345. ;;
  2346. 0) echo -e "\n${C_YELLOW}❌ Cancelled.${C_RESET}"; return ;;
  2347. *) echo -e "\n${C_RED}❌ Invalid option.${C_RESET}"; return ;;
  2348. esac
  2349. # Username
  2350. local rand_suffix=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 5)
  2351. local default_username="trial_${rand_suffix}"
  2352. read -p "👤 Username [${default_username}]: " username
  2353. username=${username:-$default_username}
  2354. if id "$username" &>/dev/null || grep -q "^$username:" "$DB_FILE"; then
  2355. echo -e "\n${C_RED}❌ Error: User '$username' already exists.${C_RESET}"; return
  2356. fi
  2357. # Password
  2358. local password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
  2359. read -p "🔑 Password [${password}]: " custom_pass
  2360. password=${custom_pass:-$password}
  2361. # Connection limit
  2362. read -p "📶 Connection limit [1]: " limit
  2363. limit=${limit:-1}
  2364. if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  2365. # Bandwidth limit
  2366. read -p "📦 Bandwidth limit in GB (0 = unlimited) [0]: " bandwidth_gb
  2367. bandwidth_gb=${bandwidth_gb:-0}
  2368. if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  2369. # Calculate expiry
  2370. local expire_date
  2371. if [[ "$duration_hours" -ge 24 ]]; then
  2372. local days=$((duration_hours / 24))
  2373. expire_date=$(date -d "+$days days" +%Y-%m-%d)
  2374. else
  2375. # For sub-day durations, set expiry to tomorrow to be safe (at job does the real cleanup)
  2376. expire_date=$(date -d "+1 day" +%Y-%m-%d)
  2377. fi
  2378. local expiry_timestamp
  2379. expiry_timestamp=$(date -d "+${duration_hours} hours" '+%Y-%m-%d %H:%M:%S')
  2380. # Create the system user
  2381. useradd -m -s /usr/sbin/nologin "$username"
  2382. usermod -aG ffusers "$username" 2>/dev/null
  2383. echo "$username:$password" | chpasswd
  2384. chage -E "$expire_date" "$username"
  2385. echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
  2386. # Schedule auto-cleanup via 'at'
  2387. echo "$TRIAL_CLEANUP_SCRIPT $username" | at now + ${duration_hours} hours 2>/dev/null
  2388. local bw_display="Unlimited"
  2389. if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
  2390. clear; show_banner
  2391. echo -e "${C_GREEN}✅ Trial account created successfully!${C_RESET}\n"
  2392. echo -e "${C_YELLOW}========================================${C_RESET}"
  2393. echo -e " ⏱️ ${C_BOLD}TRIAL ACCOUNT${C_RESET}"
  2394. echo -e "${C_YELLOW}========================================${C_RESET}"
  2395. echo -e " - 👤 Username: ${C_YELLOW}$username${C_RESET}"
  2396. echo -e " - 🔑 Password: ${C_YELLOW}$password${C_RESET}"
  2397. echo -e " - ⏱️ Duration: ${C_CYAN}$duration_label${C_RESET}"
  2398. echo -e " - 🕐 Auto-expires at: ${C_RED}$expiry_timestamp${C_RESET}"
  2399. echo -e " - 📶 Connection Limit: ${C_YELLOW}$limit${C_RESET}"
  2400. echo -e " - 📦 Bandwidth Limit: ${C_YELLOW}$bw_display${C_RESET}"
  2401. echo -e "${C_YELLOW}========================================${C_RESET}"
  2402. echo -e "\n${C_DIM}The account will be automatically deleted when the trial expires.${C_RESET}"
  2403. # Auto-ask for config generation
  2404. echo
  2405. read -p "👉 Generate client config for this trial user? (y/n): " gen_conf
  2406. if [[ "$gen_conf" == "y" || "$gen_conf" == "Y" ]]; then
  2407. generate_client_config "$username" "$password"
  2408. fi
  2409. update_ssh_banners_config
  2410. }
  2411. view_user_bandwidth() {
  2412. _select_user_interface "--- 📊 View User Bandwidth ---"
  2413. local u=$SELECTED_USER
  2414. if [[ "$u" == "NO_USERS" || -z "$u" ]]; then return; fi
  2415. clear; show_banner
  2416. echo -e "${C_BOLD}${C_PURPLE}--- 📊 Bandwidth Details: ${C_YELLOW}$u${C_PURPLE} ---${C_RESET}\n"
  2417. local line; line=$(grep "^$u:" "$DB_FILE")
  2418. local bandwidth_gb; bandwidth_gb=$(echo "$line" | cut -d: -f5)
  2419. [[ -z "$bandwidth_gb" ]] && bandwidth_gb="0"
  2420. local used_bytes=0
  2421. if [[ -f "$BANDWIDTH_DIR/${u}.usage" ]]; then
  2422. used_bytes=$(cat "$BANDWIDTH_DIR/${u}.usage" 2>/dev/null)
  2423. [[ -z "$used_bytes" ]] && used_bytes=0
  2424. fi
  2425. local used_mb; used_mb=$(awk "BEGIN {printf \"%.2f\", $used_bytes / 1048576}")
  2426. local used_gb; used_gb=$(awk "BEGIN {printf \"%.3f\", $used_bytes / 1073741824}")
  2427. echo -e " ${C_CYAN}Data Used:${C_RESET} ${C_WHITE}${used_gb} GB${C_RESET} (${used_mb} MB)"
  2428. if [[ "$bandwidth_gb" == "0" ]]; then
  2429. echo -e " ${C_CYAN}Bandwidth Limit:${C_RESET} ${C_GREEN}Unlimited${C_RESET}"
  2430. echo -e " ${C_CYAN}Status:${C_RESET} ${C_GREEN}No quota restrictions${C_RESET}"
  2431. else
  2432. local quota_bytes; quota_bytes=$(awk "BEGIN {printf \"%.0f\", $bandwidth_gb * 1073741824}")
  2433. local percentage; percentage=$(awk "BEGIN {printf \"%.1f\", ($used_bytes / $quota_bytes) * 100}")
  2434. local remaining_bytes; remaining_bytes=$((quota_bytes - used_bytes))
  2435. if [[ "$remaining_bytes" -lt 0 ]]; then remaining_bytes=0; fi
  2436. local remaining_gb; remaining_gb=$(awk "BEGIN {printf \"%.3f\", $remaining_bytes / 1073741824}")
  2437. echo -e " ${C_CYAN}Bandwidth Limit:${C_RESET} ${C_YELLOW}${bandwidth_gb} GB${C_RESET}"
  2438. echo -e " ${C_CYAN}Remaining:${C_RESET} ${C_WHITE}${remaining_gb} GB${C_RESET}"
  2439. echo -e " ${C_CYAN}Usage:${C_RESET} ${C_WHITE}${percentage}%${C_RESET}"
  2440. # Progress bar
  2441. local bar_width=30
  2442. local filled; filled=$(awk "BEGIN {printf \"%.0f\", ($percentage / 100) * $bar_width}")
  2443. if [[ "$filled" -gt "$bar_width" ]]; then filled=$bar_width; fi
  2444. local empty=$((bar_width - filled))
  2445. local bar_color="$C_GREEN"
  2446. if (( $(awk "BEGIN {print ($percentage > 80)}" ) )); then bar_color="$C_RED"
  2447. elif (( $(awk "BEGIN {print ($percentage > 50)}" ) )); then bar_color="$C_YELLOW"
  2448. fi
  2449. printf " ${C_CYAN}Progress:${C_RESET} ${bar_color}["
  2450. for ((i=0; i<filled; i++)); do printf "█"; done
  2451. for ((i=0; i<empty; i++)); do printf "░"; done
  2452. printf "]${C_RESET} ${percentage}%%\n"
  2453. if [[ "$used_bytes" -ge "$quota_bytes" ]]; then
  2454. echo -e "\n ${C_RED}⚠️ USER HAS EXCEEDED BANDWIDTH QUOTA — ACCOUNT LOCKED${C_RESET}"
  2455. fi
  2456. fi
  2457. }
  2458. bulk_create_users() {
  2459. clear; show_banner
  2460. echo -e "${C_BOLD}${C_PURPLE}--- 👥 Bulk Create Users ---${C_RESET}"
  2461. read -p "👉 Enter username prefix (e.g., 'user'): " prefix
  2462. if [[ -z "$prefix" ]]; then echo -e "\n${C_RED}❌ Prefix cannot be empty.${C_RESET}"; return; fi
  2463. read -p "🔢 How many users to create? " count
  2464. if ! [[ "$count" =~ ^[0-9]+$ ]] || [[ "$count" -lt 1 ]] || [[ "$count" -gt 100 ]]; then
  2465. echo -e "\n${C_RED}❌ Invalid count (1-100).${C_RESET}"; return
  2466. fi
  2467. read -p "🗓️ Account duration (in days) [30]: " days
  2468. days=${days:-30}
  2469. if ! [[ "$days" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  2470. read -p "📶 Connection limit per user [1]: " limit
  2471. limit=${limit:-1}
  2472. if ! [[ "$limit" =~ ^[0-9]+$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  2473. read -p "📦 Bandwidth limit in GB per user (0 = unlimited) [0]: " bandwidth_gb
  2474. bandwidth_gb=${bandwidth_gb:-0}
  2475. if ! [[ "$bandwidth_gb" =~ ^[0-9]+\.?[0-9]*$ ]]; then echo -e "\n${C_RED}❌ Invalid number.${C_RESET}"; return; fi
  2476. local expire_date
  2477. expire_date=$(date -d "+$days days" +%Y-%m-%d)
  2478. local bw_display="Unlimited"; [[ "$bandwidth_gb" != "0" ]] && bw_display="${bandwidth_gb} GB"
  2479. echo -e "\n${C_BLUE}⚙️ Creating $count users with prefix '${prefix}'...${C_RESET}\n"
  2480. echo -e "${C_YELLOW}================================================================${C_RESET}"
  2481. printf "${C_BOLD}${C_WHITE}%-20s | %-15s | %-12s${C_RESET}\n" "USERNAME" "PASSWORD" "EXPIRES"
  2482. echo -e "${C_YELLOW}----------------------------------------------------------------${C_RESET}"
  2483. local created=0
  2484. for ((i=1; i<=count; i++)); do
  2485. local username="${prefix}${i}"
  2486. if id "$username" &>/dev/null || grep -q "^$username:" "$DB_FILE"; then
  2487. echo -e "${C_RED} ⚠️ Skipping '$username' — already exists${C_RESET}"
  2488. continue
  2489. fi
  2490. local password=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 8)
  2491. useradd -m -s /usr/sbin/nologin "$username"
  2492. usermod -aG ffusers "$username" 2>/dev/null
  2493. echo "$username:$password" | chpasswd
  2494. chage -E "$expire_date" "$username"
  2495. echo "$username:$password:$expire_date:$limit:$bandwidth_gb" >> "$DB_FILE"
  2496. printf " ${C_GREEN}%-20s${C_RESET} | ${C_YELLOW}%-15s${C_RESET} | ${C_CYAN}%-12s${C_RESET}\n" "$username" "$password" "$expire_date"
  2497. created=$((created + 1))
  2498. done
  2499. echo -e "${C_YELLOW}================================================================${C_RESET}"
  2500. echo -e "\n${C_GREEN}✅ Created $created users. Conn Limit: ${limit} | BW: ${bw_display}${C_RESET}"
  2501. update_ssh_banners_config
  2502. }
  2503. generate_client_config() {
  2504. local user=$1
  2505. local pass=$2
  2506. # Auto-detect Host
  2507. local host_ip=$(curl -s -4 icanhazip.com)
  2508. local host_domain="$host_ip"
  2509. if [ -f "$DNS_INFO_FILE" ]; then
  2510. local managed_domain=$(grep 'FULL_DOMAIN' "$DNS_INFO_FILE" | cut -d'"' -f2)
  2511. if [[ -n "$managed_domain" ]]; then host_domain="$managed_domain"; fi
  2512. fi
  2513. # Also check if HAProxy Certbot is used
  2514. if [ -f "$NGINX_CONFIG_FILE" ]; then
  2515. local nginx_domain=$(grep -oP 'server_name \K[^\s;]+' "$NGINX_CONFIG_FILE" | head -n 1)
  2516. if [[ "$nginx_domain" != "_" && -n "$nginx_domain" ]]; then host_domain="$nginx_domain"; fi
  2517. fi
  2518. echo -e "\n${C_BOLD}${C_PURPLE}--- 📱 Client Connection Configuration ---${C_RESET}"
  2519. echo -e "${C_CYAN}Copy the details below to your clipboard:${C_RESET}\n"
  2520. echo -e "${C_YELLOW}========================================${C_RESET}"
  2521. echo -e "👤 ${C_BOLD}User Details${C_RESET}"
  2522. echo -e " • Username: ${C_WHITE}$user${C_RESET}"
  2523. echo -e " • Password: ${C_WHITE}$pass${C_RESET}"
  2524. echo -e " • Host/IP : ${C_WHITE}$host_domain${C_RESET}"
  2525. echo -e "${C_YELLOW}========================================${C_RESET}"
  2526. # 1. SSH Direct
  2527. echo -e "\n🔹 ${C_BOLD}SSH Direct${C_RESET}:"
  2528. echo -e " • Host: $host_domain"
  2529. echo -e " • Port: 22"
  2530. echo -e " • payload: (Standard SSH)"
  2531. # 2. SSL/TLS Tunnel (HAProxy Multiplexed)
  2532. if systemctl is-active --quiet haproxy; then
  2533. echo -e "\n🔹 ${C_BOLD}SSL/TLS Tunnel (Multiplexed HAProxy)${C_RESET}:"
  2534. echo -e " • Host: $host_domain"
  2535. echo -e " • Port(s): 80 (HTTP/SSH), 443 (TLS/SSH)"
  2536. echo -e " • SNI (BugHost): $host_domain (or your preferred SNI)"
  2537. fi
  2538. # 3. UDP Custom
  2539. if systemctl is-active --quiet udp-custom; then
  2540. echo -e "\n🔹 ${C_BOLD}UDP Custom${C_RESET}:"
  2541. echo -e " • IP: $host_ip (Must use numeric IP)"
  2542. echo -e " • Port: 1-65535 (Exclude 53, 5300)"
  2543. echo -e " • Obfs: (None/Plain)"
  2544. fi
  2545. # 4. DNSTT
  2546. if systemctl is-active --quiet dnstt; then
  2547. if [ -f "$DNSTT_CONFIG_FILE" ]; then
  2548. source "$DNSTT_CONFIG_FILE"
  2549. echo -e "\n🔹 ${C_BOLD}DNSTT (SlowDNS)${C_RESET}:"
  2550. echo -e " • Nameserver: $TUNNEL_DOMAIN"
  2551. echo -e " • PubKey: $PUBLIC_KEY"
  2552. echo -e " • DNS IP: 1.1.1.1 / 8.8.8.8"
  2553. fi
  2554. fi
  2555. # 5. ZiVPN
  2556. if systemctl is-active --quiet zivpn; then
  2557. echo -e "\n🔹 ${C_BOLD}ZiVPN${C_RESET}:"
  2558. echo -e " • UDP Port: 5667"
  2559. echo -e " • Forwarded Ports: 6000-19999"
  2560. fi
  2561. echo -e "${C_YELLOW}========================================${C_RESET}"
  2562. press_enter
  2563. }
  2564. client_config_menu() {
  2565. _select_user_interface "--- 📱 Generate Client Config ---"
  2566. local u=$SELECTED_USER
  2567. if [[ "$u" == "NO_USERS" || -z "$u" ]]; then return; fi
  2568. # We need to find the password. It's in the DB.
  2569. local pass=$(grep "^$u:" "$DB_FILE" | cut -d: -f2)
  2570. generate_client_config "$u" "$pass"
  2571. }
  2572. # Lightweight Bash Monitor (No vnStat required)
  2573. simple_live_monitor() {
  2574. local iface=$1
  2575. echo -e "\n${C_BLUE}⚡ Starting Lightweight Traffic Monitor for $iface...${C_RESET}"
  2576. echo -e "${C_DIM}Press [Ctrl+C] to stop.${C_RESET}\n"
  2577. # Get initial values
  2578. local rx1=$(cat /sys/class/net/$iface/statistics/rx_bytes)
  2579. local tx1=$(cat /sys/class/net/$iface/statistics/tx_bytes)
  2580. printf "%-15s | %-15s\n" "⬇️ Download" "⬆️ Upload"
  2581. echo "-----------------------------------"
  2582. while true; do
  2583. sleep 1
  2584. local rx2=$(cat /sys/class/net/$iface/statistics/rx_bytes)
  2585. local tx2=$(cat /sys/class/net/$iface/statistics/tx_bytes)
  2586. # Calculate diffs
  2587. local rx_diff=$((rx2 - rx1))
  2588. local tx_diff=$((tx2 - tx1))
  2589. # Convert to KB/s
  2590. local rx_kbs=$((rx_diff / 1024))
  2591. local tx_kbs=$((tx_diff / 1024))
  2592. # Formatting for MB/s if > 1024 KB
  2593. if [ $rx_kbs -gt 1024 ]; then rx_fmt=$(awk "BEGIN {printf \"%.2f MB/s\", $rx_kbs/1024}"); else rx_fmt="${rx_kbs} KB/s"; fi
  2594. if [ $tx_kbs -gt 1024 ]; then tx_fmt=$(awk "BEGIN {printf \"%.2f MB/s\", $tx_kbs/1024}"); else tx_fmt="${tx_kbs} KB/s"; fi
  2595. printf "\r%-15s | %-15s" "$rx_fmt" "$tx_fmt"
  2596. # Reset for next loop
  2597. rx1=$rx2
  2598. tx1=$tx2
  2599. done
  2600. }
  2601. traffic_monitor_menu() {
  2602. clear; show_banner
  2603. echo -e "${C_BOLD}${C_PURPLE}--- 📈 Network Traffic Monitor ---${C_RESET}"
  2604. # Find active interface
  2605. local iface=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
  2606. echo -e "\nInterface: ${C_CYAN}${iface}${C_RESET}"
  2607. echo -e "\n${C_BOLD}Select a monitoring option:${C_RESET}\n"
  2608. printf " ${C_CHOICE}[ 1]${C_RESET} %-40s\n" "⚡ Live Monitor ${C_DIM}(Lightweight, No Install)${C_RESET}"
  2609. printf " ${C_CHOICE}[ 2]${C_RESET} %-40s\n" "📊 View Total Traffic Since Boot"
  2610. printf " ${C_CHOICE}[ 3]${C_RESET} %-40s\n" "📅 Daily/Monthly Logs ${C_DIM}(Requires vnStat)${C_RESET}"
  2611. echo -e "\n ${C_WARN}[ 0]${C_RESET} ↩️ Return"
  2612. echo
  2613. read -p "👉 Enter choice: " t_choice
  2614. case $t_choice in
  2615. 1)
  2616. simple_live_monitor "$iface"
  2617. ;;
  2618. 2)
  2619. local rx_total=$(cat /sys/class/net/$iface/statistics/rx_bytes)
  2620. local tx_total=$(cat /sys/class/net/$iface/statistics/tx_bytes)
  2621. local rx_mb=$((rx_total / 1024 / 1024))
  2622. local tx_mb=$((tx_total / 1024 / 1024))
  2623. echo -e "\n${C_BLUE}📊 Total Traffic (Since Boot):${C_RESET}"
  2624. echo -e " ⬇️ Download: ${C_WHITE}${rx_mb} MB${C_RESET}"
  2625. echo -e " ⬆️ Upload: ${C_WHITE}${tx_mb} MB${C_RESET}"
  2626. press_enter
  2627. ;;
  2628. 3)
  2629. # vnStat Logic
  2630. if ! command -v vnstat &> /dev/null; then
  2631. echo -e "\n${C_YELLOW}⚠️ vnStat is not installed.${C_RESET}"
  2632. echo -e " This tool provides persistent history (Daily/Monthly reports)."
  2633. echo -e " It is lightweight but requires installation."
  2634. read -p "👉 Install vnStat now? (y/n): " confirm
  2635. if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
  2636. echo -e "\n${C_BLUE}📦 Installing vnStat...${C_RESET}"
  2637. apt-get update >/dev/null 2>&1
  2638. apt-get install -y vnstat >/dev/null 2>&1
  2639. systemctl enable vnstat >/dev/null 2>&1
  2640. systemctl restart vnstat >/dev/null 2>&1
  2641. local default_iface=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -1)
  2642. vnstat --add -i "$default_iface" >/dev/null 2>&1
  2643. echo -e "${C_GREEN}✅ Installed.${C_RESET}"
  2644. sleep 1
  2645. else
  2646. return
  2647. fi
  2648. fi
  2649. echo
  2650. vnstat -i "$iface"
  2651. echo -e "\n${C_DIM}Run 'vnstat -d' or 'vnstat -m' manually for specific views.${C_RESET}"
  2652. press_enter
  2653. ;;
  2654. *) return ;;
  2655. esac
  2656. }
  2657. torrent_block_menu() {
  2658. clear; show_banner
  2659. echo -e "${C_BOLD}${C_PURPLE}--- 🚫 Torrent Blocking (Anti-Torrent) ---${C_RESET}"
  2660. # Check status
  2661. local torrent_status="${C_STATUS_I}Disabled${C_RESET}"
  2662. if iptables -L FORWARD | grep -q "ipp2p"; then
  2663. torrent_status="${C_STATUS_A}Enabled${C_RESET}"
  2664. elif iptables -L OUTPUT | grep -q "BitTorrent"; then
  2665. # Fallback check for string matching
  2666. torrent_status="${C_STATUS_A}Enabled${C_RESET}"
  2667. fi
  2668. echo -e "\n${C_WHITE}Current Status: ${torrent_status}${C_RESET}"
  2669. echo -e "${C_DIM}This feature uses iptables string matching to block common torrent keywords.${C_RESET}"
  2670. echo -e "\n${C_BOLD}Select an action:${C_RESET}\n"
  2671. printf " ${C_CHOICE}[ 1]${C_RESET} %-40s\n" "🔒 Enable Torrent Blocking"
  2672. printf " ${C_CHOICE}[ 2]${C_RESET} %-40s\n" "🔓 Disable Torrent Blocking"
  2673. echo -e "\n ${C_WARN}[ 0]${C_RESET} ↩️ Return"
  2674. echo
  2675. read -p "👉 Enter choice: " b_choice
  2676. case $b_choice in
  2677. 1)
  2678. echo -e "\n${C_BLUE}🛡️ Applying Anti-Torrent rules...${C_RESET}"
  2679. # Clean old rules first to avoid duplicates
  2680. _flush_torrent_rules
  2681. # Block Common Torrent Ports/Keywords
  2682. # String matching using iptables extension
  2683. iptables -A FORWARD -m string --string "BitTorrent" --algo bm -j DROP
  2684. iptables -A FORWARD -m string --string "BitTorrent protocol" --algo bm -j DROP
  2685. iptables -A FORWARD -m string --string "peer_id=" --algo bm -j DROP
  2686. iptables -A FORWARD -m string --string ".torrent" --algo bm -j DROP
  2687. iptables -A FORWARD -m string --string "announce.php?passkey=" --algo bm -j DROP
  2688. iptables -A FORWARD -m string --string "torrent" --algo bm -j DROP
  2689. iptables -A FORWARD -m string --string "info_hash" --algo bm -j DROP
  2690. iptables -A FORWARD -m string --string "get_peers" --algo bm -j DROP
  2691. iptables -A FORWARD -m string --string "find_node" --algo bm -j DROP
  2692. # Same for OUTPUT to be safe
  2693. iptables -A OUTPUT -m string --string "BitTorrent" --algo bm -j DROP
  2694. iptables -A OUTPUT -m string --string "BitTorrent protocol" --algo bm -j DROP
  2695. iptables -A OUTPUT -m string --string "peer_id=" --algo bm -j DROP
  2696. iptables -A OUTPUT -m string --string ".torrent" --algo bm -j DROP
  2697. iptables -A OUTPUT -m string --string "announce.php?passkey=" --algo bm -j DROP
  2698. iptables -A OUTPUT -m string --string "torrent" --algo bm -j DROP
  2699. iptables -A OUTPUT -m string --string "info_hash" --algo bm -j DROP
  2700. iptables -A OUTPUT -m string --string "get_peers" --algo bm -j DROP
  2701. iptables -A OUTPUT -m string --string "find_node" --algo bm -j DROP
  2702. # Attempt to save if iptables-persistent exists
  2703. if dpkg -s iptables-persistent &>/dev/null; then
  2704. netfilter-persistent save &>/dev/null
  2705. fi
  2706. echo -e "${C_GREEN}✅ Torrent Blocking Enabled.${C_RESET}"
  2707. press_enter
  2708. ;;
  2709. 2)
  2710. echo -e "\n${C_BLUE}🔓 Removing Anti-Torrent rules...${C_RESET}"
  2711. _flush_torrent_rules
  2712. if dpkg -s iptables-persistent &>/dev/null; then
  2713. netfilter-persistent save &>/dev/null
  2714. fi
  2715. echo -e "${C_GREEN}✅ Torrent Blocking Disabled.${C_RESET}"
  2716. press_enter
  2717. ;;
  2718. *) return ;;
  2719. esac
  2720. }
  2721. _flush_torrent_rules() {
  2722. # Helper to remove rules containing specific strings
  2723. # This is a bit brute-force but effective for this script's scope
  2724. iptables -D FORWARD -m string --string "BitTorrent" --algo bm -j DROP 2>/dev/null
  2725. iptables -D FORWARD -m string --string "BitTorrent protocol" --algo bm -j DROP 2>/dev/null
  2726. iptables -D FORWARD -m string --string "peer_id=" --algo bm -j DROP 2>/dev/null
  2727. iptables -D FORWARD -m string --string ".torrent" --algo bm -j DROP 2>/dev/null
  2728. iptables -D FORWARD -m string --string "announce.php?passkey=" --algo bm -j DROP 2>/dev/null
  2729. iptables -D FORWARD -m string --string "torrent" --algo bm -j DROP 2>/dev/null
  2730. iptables -D FORWARD -m string --string "info_hash" --algo bm -j DROP 2>/dev/null
  2731. iptables -D FORWARD -m string --string "get_peers" --algo bm -j DROP 2>/dev/null
  2732. iptables -D FORWARD -m string --string "find_node" --algo bm -j DROP 2>/dev/null
  2733. iptables -D OUTPUT -m string --string "BitTorrent" --algo bm -j DROP 2>/dev/null
  2734. iptables -D OUTPUT -m string --string "BitTorrent protocol" --algo bm -j DROP 2>/dev/null
  2735. iptables -D OUTPUT -m string --string "peer_id=" --algo bm -j DROP 2>/dev/null
  2736. iptables -D OUTPUT -m string --string ".torrent" --algo bm -j DROP 2>/dev/null
  2737. iptables -D OUTPUT -m string --string "announce.php?passkey=" --algo bm -j DROP 2>/dev/null
  2738. iptables -D OUTPUT -m string --string "torrent" --algo bm -j DROP 2>/dev/null
  2739. iptables -D OUTPUT -m string --string "info_hash" --algo bm -j DROP 2>/dev/null
  2740. iptables -D OUTPUT -m string --string "get_peers" --algo bm -j DROP 2>/dev/null
  2741. iptables -D OUTPUT -m string --string "find_node" --algo bm -j DROP 2>/dev/null
  2742. }
  2743. ssh_banner_menu() {
  2744. clear; show_banner
  2745. echo -e "${C_BOLD}${C_PURPLE}--- 🎨 SSH Login Banner ---${C_RESET}"
  2746. echo -e "\n${C_CYAN}When enabled, users connecting via SSH tunnel (HTTP Custom,"
  2747. echo -e "HTTP Injector, etc.) will see their account details:${C_RESET}"
  2748. echo -e " • Days/hours remaining"
  2749. echo -e " • Bandwidth used and remaining"
  2750. echo -e " • Active sessions count"
  2751. # Check current status
  2752. if [[ -f "$SSHD_FF_CONFIG" ]]; then
  2753. echo -e "\n Status: ${C_GREEN}✅ ENABLED${C_RESET}"
  2754. else
  2755. echo -e "\n Status: ${C_RED}❌ DISABLED${C_RESET}"
  2756. fi
  2757. echo -e "\n${C_BOLD}Options:${C_RESET}\n"
  2758. printf " ${C_CHOICE}[ 1]${C_RESET} %-35s\n" "✅ Enable Login Banner"
  2759. printf " ${C_CHOICE}[ 2]${C_RESET} %-35s\n" "❌ Disable Login Banner"
  2760. printf " ${C_CHOICE}[ 3]${C_RESET} %-35s\n" "📝 Preview Banner (test with a user)"
  2761. echo -e "\n ${C_WARN}[ 0]${C_RESET} ↩️ Return"
  2762. echo
  2763. read -p "👉 Enter choice: " banner_choice
  2764. case $banner_choice in
  2765. 1)
  2766. touch "/etc/firewallfalcon/banners_enabled"
  2767. update_ssh_banners_config
  2768. echo -e "\n${C_GREEN}✅ SSH Login Banner has been enabled!${C_RESET}"
  2769. echo -e "${C_DIM}Users will see account info when they connect via SSH tunnel.${C_RESET}"
  2770. press_enter
  2771. ;;
  2772. 2)
  2773. rm -f "/etc/firewallfalcon/banners_enabled"
  2774. update_ssh_banners_config
  2775. echo -e "\n${C_YELLOW}❌ SSH Login Banner has been disabled.${C_RESET}"
  2776. press_enter
  2777. ;;
  2778. 3)
  2779. # Force background service to regenerate to ensure the syntax error fix is applied
  2780. # even if the user didn't run the --install-setup command!
  2781. echo -e "${C_DIM}Re-syncing background limiter service...${C_RESET}"
  2782. setup_limiter_service >/dev/null 2>&1
  2783. if [[ ! -f "/etc/firewallfalcon/banners_enabled" ]]; then
  2784. echo -e "\n${C_RED}❌ You must ENABLE the Login Banner (Option 1) before you can preview it!${C_RESET}"
  2785. echo -e "${C_YELLOW}The service only generates these files while the feature is active.${C_RESET}"
  2786. press_enter
  2787. return
  2788. fi
  2789. _select_user_interface "--- 📝 Preview Login Banner ---"
  2790. local u=$SELECTED_USER
  2791. if [[ -z "$u" || "$u" == "NO_USERS" ]]; then return; fi
  2792. echo -e "\n${C_CYAN}--- Banner Preview for user '$u' ---${C_RESET}\n"
  2793. if [[ -f "/etc/firewallfalcon/banners/${u}.txt" ]]; then
  2794. cat "/etc/firewallfalcon/banners/${u}.txt"
  2795. else
  2796. echo -e "${C_RED}Banner file not generated yet. Waiting up to 15s for limiter to write it...${C_RESET}"
  2797. sleep 5
  2798. if ! cat "/etc/firewallfalcon/banners/${u}.txt" 2>/dev/null; then
  2799. echo -e "\n${C_RED}Still not generated. The limiter service MUST be crashing! Here is the error log:${C_RESET}"
  2800. echo -e "----------------------------------------------------------------------"
  2801. journalctl -u firewallfalcon-limiter -n 15 --no-pager
  2802. echo -e "----------------------------------------------------------------------"
  2803. fi
  2804. fi
  2805. press_enter
  2806. ;;
  2807. *) return ;;
  2808. esac
  2809. }
  2810. auto_reboot_menu() {
  2811. clear; show_banner
  2812. echo -e "${C_BOLD}${C_PURPLE}--- 🔄 Auto-Reboot Management ---${C_RESET}"
  2813. # Check status
  2814. local cron_check=$(crontab -l 2>/dev/null | grep "systemctl reboot")
  2815. local status="${C_STATUS_I}Disabled${C_RESET}"
  2816. if [[ -n "$cron_check" ]]; then
  2817. status="${C_STATUS_A}Active (Midnight)${C_RESET}"
  2818. fi
  2819. echo -e "\n${C_WHITE}Current Status: ${status}${C_RESET}"
  2820. echo -e "\n${C_BOLD}Select an action:${C_RESET}\n"
  2821. printf " ${C_CHOICE}[ 1]${C_RESET} %-40s\n" "🕐 Enable Daily Reboot (00:00 midnight)"
  2822. printf " ${C_CHOICE}[ 2]${C_RESET} %-40s\n" "❌ Disable Auto-Reboot"
  2823. echo -e "\n ${C_WARN}[ 0]${C_RESET} ↩️ Return"
  2824. echo
  2825. read -p "👉 Enter choice: " r_choice
  2826. case $r_choice in
  2827. 1)
  2828. # Remove existing to prevent duplicates
  2829. (crontab -l 2>/dev/null | grep -v "systemctl reboot") | crontab -
  2830. # Add new job
  2831. (crontab -l 2>/dev/null; echo "0 0 * * * systemctl reboot") | crontab -
  2832. echo -e "\n${C_GREEN}✅ Auto-reboot scheduled for every day at 00:00.${C_RESET}"
  2833. press_enter
  2834. ;;
  2835. 2)
  2836. (crontab -l 2>/dev/null | grep -v "systemctl reboot") | crontab -
  2837. echo -e "\n${C_GREEN}✅ Auto-reboot disabled.${C_RESET}"
  2838. press_enter
  2839. ;;
  2840. *) return ;;
  2841. esac
  2842. }
  2843. press_enter() {
  2844. echo -e "\nPress ${C_YELLOW}[Enter]${C_RESET} to return to the menu..." && read -r
  2845. }
  2846. invalid_option() {
  2847. echo -e "\n${C_RED}❌ Invalid option.${C_RESET}" && sleep 1
  2848. }
  2849. main_menu() {
  2850. while true; do
  2851. export UNINSTALL_MODE="interactive"
  2852. show_banner
  2853. echo
  2854. echo -e " ${C_TITLE}═══════════════════[ ${C_BOLD}👤 USER MANAGEMENT ${C_RESET}${C_TITLE}]═══════════════════${C_RESET}"
  2855. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "1" "✨ Create New User" "6" "✏️ Edit User Details"
  2856. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "2" "🗑️ Delete User" "7" "📋 List Managed Users"
  2857. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "3" "🔄 Renew User Account" "8" "📱 Generate Client Config"
  2858. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "4" "🔒 Lock User Account" "9" "⏱️ Create Trial Account"
  2859. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "5" "🔓 Unlock User Account" "10" "📊 View User Bandwidth"
  2860. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "11" "👥 Bulk Create Users"
  2861. echo
  2862. echo -e " ${C_TITLE}══════════════[ ${C_BOLD}🌐 VPN & PROTOCOLS ${C_RESET}${C_TITLE}]═══════════════${C_RESET}"
  2863. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "12" "🔌 Protocol Manager" "13" "📈 Traffic Monitor (Lite)"
  2864. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "14" "🚫 Block Torrent (Anti-P2P)"
  2865. echo
  2866. echo -e " ${C_TITLE}══════════════[ ${C_BOLD}⚙️ SYSTEM SETTINGS ${C_RESET}${C_TITLE}]═══════════════${C_RESET}"
  2867. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "15" "☁️ CloudFlare Free Domain" "18" "💾 Backup User Data"
  2868. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "16" "🎨 SSH Banner Config" "19" "📥 Restore User Data"
  2869. printf " ${C_CHOICE}[%2s]${C_RESET} %-28s ${C_CHOICE}[%2s]${C_RESET} %-28s\n" "17" "🔄 Auto-Reboot Task" "20" "🧹 Cleanup Expired Users"
  2870. echo
  2871. echo -e " ${C_DANGER}═══════════════════[ ${C_BOLD}🔥 DANGER ZONE ${C_RESET}${C_DANGER}]═══════════════════${C_RESET}"
  2872. echo -e " ${C_DANGER}[99]${C_RESET} Uninstall Script ${C_WARN}[ 0]${C_RESET} Exit"
  2873. echo
  2874. read -p "$(echo -e ${C_PROMPT}"👉 Select an option: "${C_RESET})" choice
  2875. case $choice in
  2876. 1) create_user; press_enter ;;
  2877. 2) delete_user; press_enter ;;
  2878. 3) renew_user; press_enter ;;
  2879. 4) lock_user; press_enter ;;
  2880. 5) unlock_user; press_enter ;;
  2881. 6) edit_user; press_enter ;;
  2882. 7) list_users; press_enter ;;
  2883. 8) client_config_menu; press_enter ;;
  2884. 9) create_trial_account; press_enter ;;
  2885. 10) view_user_bandwidth; press_enter ;;
  2886. 11) bulk_create_users; press_enter ;;
  2887. 12) protocol_menu ;;
  2888. 13) traffic_monitor_menu ;;
  2889. 14) torrent_block_menu ;;
  2890. 15) dns_menu; press_enter ;;
  2891. 16) ssh_banner_menu ;;
  2892. 17) auto_reboot_menu ;;
  2893. 18) backup_user_data; press_enter ;;
  2894. 19) restore_user_data; press_enter ;;
  2895. 20) cleanup_expired; press_enter ;;
  2896. 99) uninstall_script ;;
  2897. 0) exit 0 ;;
  2898. *) invalid_option ;;
  2899. esac
  2900. done
  2901. }
  2902. if [[ "$1" == "--install-setup" ]]; then
  2903. initial_setup
  2904. exit 0
  2905. fi
  2906. main_menu