menu.sh 145 KB

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