menu.sh 160 KB

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