main.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package main
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "flag"
  6. "fmt"
  7. "log"
  8. "net"
  9. "net/http"
  10. "os"
  11. "os/signal"
  12. release "debtik"
  13. "sync"
  14. "syscall"
  15. "time"
  16. "debtik/internal/api"
  17. "debtik/internal/auth"
  18. "debtik/internal/bridge"
  19. "debtik/internal/bwtest"
  20. "debtik/internal/certs"
  21. "debtik/internal/cli"
  22. "debtik/internal/dhcp"
  23. "debtik/internal/dns"
  24. "debtik/internal/dynamicrouting"
  25. "debtik/internal/firewall"
  26. "debtik/internal/logging"
  27. "debtik/internal/network"
  28. "debtik/internal/reverseproxy"
  29. "debtik/internal/routing"
  30. "debtik/internal/services"
  31. "debtik/internal/tools"
  32. "debtik/internal/vpn"
  33. "debtik/internal/vrrp"
  34. "debtik/internal/web"
  35. )
  36. func main() {
  37. addr := flag.String("addr", ":8080", "HTTP service address (e.g. :8080 or :80)")
  38. cliAddr := flag.String("cli-addr", ":8282", "CLI service address for Telnet and debtik-cli (e.g. :8282)")
  39. metaPath := flag.String("meta", "/etc/debtik/metadata.json", "Path to interface metadata storage file")
  40. flag.Parse()
  41. if os.Geteuid() == 0 {
  42. if err := release.RefreshUpdateHelpers("/usr/local/libexec"); err != nil {
  43. log.Printf("Cannot refresh updater helpers: %v", err)
  44. }
  45. }
  46. // Initialize Logging Ring Buffer
  47. _ = logging.GetLogger()
  48. // Initialize components
  49. metaMgr := network.NewMetadataManager(*metaPath)
  50. netMgr := network.NewManager(metaMgr)
  51. persistMgr := network.NewPersistenceManager(netMgr)
  52. vpnMgr := vpn.NewManager()
  53. defer vpnMgr.AeroTUN.StopAll()
  54. fwMgr := firewall.NewFirewallManager()
  55. routingMgr := routing.NewRoutingManager()
  56. defer routingMgr.Stop()
  57. dynRoutingMgr := dynamicrouting.NewManager("")
  58. bridgeMgr := bridge.NewBridgeManager()
  59. toolsMgr := tools.NewManager()
  60. dhcpMgr := dhcp.NewManager()
  61. dhcpServerMgr := dhcp.NewServerManager()
  62. vrrpMgr := vrrp.NewManager()
  63. bwtestMgr := bwtest.NewManager("")
  64. defer bwtestMgr.Stop()
  65. dnsMgr := dns.NewManager()
  66. authMgr := auth.NewManager("")
  67. servicesMgr := services.NewManager("")
  68. certMgr, err := certs.NewCertManager("")
  69. if err != nil {
  70. log.Fatalf("Failed to initialize certificate manager: %v", err)
  71. }
  72. renewCtx, stopRenewal := context.WithCancel(context.Background())
  73. defer stopRenewal()
  74. go certMgr.RunRenewal(renewCtx)
  75. proxyMgr := reverseproxy.NewManager("", certMgr)
  76. defer proxyMgr.Stop()
  77. webHandler := web.Handler()
  78. // Build Unified RouterOS CLI Executor
  79. cliExecutor := cli.NewExecutor(
  80. authMgr,
  81. netMgr,
  82. routingMgr,
  83. dynRoutingMgr,
  84. fwMgr,
  85. dhcpMgr,
  86. dhcpServerMgr,
  87. dnsMgr,
  88. bridgeMgr,
  89. vpnMgr,
  90. vrrpMgr,
  91. bwtestMgr,
  92. servicesMgr,
  93. certMgr,
  94. proxyMgr,
  95. )
  96. // Determine Telnet & SSH addresses from services config or flags
  97. telnetAddr := *cliAddr
  98. if tSvc, ok := servicesMgr.GetService("telnet"); ok && tSvc.Port > 0 {
  99. telnetAddr = fmt.Sprintf(":%d", tSvc.Port)
  100. }
  101. sshAddr := ":2222"
  102. if sSvc, ok := servicesMgr.GetService("ssh"); ok && sSvc.Port > 0 {
  103. sshAddr = fmt.Sprintf(":%d", sSvc.Port)
  104. }
  105. cliTelnetServer := cli.NewServer(telnetAddr, authMgr, cliExecutor)
  106. cliSSHServer := cli.NewSSHServer(sshAddr, authMgr, cliExecutor)
  107. // Register hooks for dynamic service start/stop & port changes
  108. servicesMgr.RegisterHook("telnet", func(serviceName string, port int, disabled bool) error {
  109. cliTelnetServer.Stop()
  110. if !disabled {
  111. cliTelnetServer.SetAddr(fmt.Sprintf(":%d", port))
  112. return waitListener(cliTelnetServer.Start, cliTelnetServer.IsRunning)
  113. }
  114. return nil
  115. })
  116. servicesMgr.RegisterHook("ssh", func(serviceName string, port int, disabled bool) error {
  117. cliSSHServer.Stop()
  118. if !disabled {
  119. cliSSHServer.SetAddr(fmt.Sprintf(":%d", port))
  120. return waitListener(cliSSHServer.Start, cliSSHServer.IsRunning)
  121. }
  122. return nil
  123. })
  124. // Start enabled CLI servers
  125. if tSvc, ok := servicesMgr.GetService("telnet"); !ok || !tSvc.Disabled {
  126. go func() {
  127. if err := cliTelnetServer.Start(); err != nil {
  128. log.Printf("[CLI-Telnet] Server listener error: %v", err)
  129. }
  130. }()
  131. }
  132. defer cliTelnetServer.Stop()
  133. if sSvc, ok := servicesMgr.GetService("ssh"); !ok || !sSvc.Disabled {
  134. go func() {
  135. if err := cliSSHServer.Start(); err != nil {
  136. log.Printf("[CLI-SSH] Server listener error: %v", err)
  137. }
  138. }()
  139. }
  140. defer cliSSHServer.Stop()
  141. // API & Web UI Server
  142. srv := api.NewServer(
  143. netMgr,
  144. persistMgr,
  145. metaMgr,
  146. vpnMgr,
  147. fwMgr,
  148. routingMgr,
  149. dynRoutingMgr,
  150. bridgeMgr,
  151. toolsMgr,
  152. dhcpMgr,
  153. dhcpServerMgr,
  154. vrrpMgr,
  155. bwtestMgr,
  156. dnsMgr,
  157. authMgr,
  158. servicesMgr,
  159. certMgr,
  160. proxyMgr,
  161. webHandler,
  162. )
  163. // Standalone HTTPS Proxy listener when www-ssl is disabled
  164. var (
  165. proxyHTTPSServer *http.Server
  166. proxyHTTPSMu sync.Mutex
  167. proxyHTTPSRunning bool
  168. )
  169. startOrRestartProxyHTTPS := func() error {
  170. proxyHTTPSMu.Lock()
  171. defer proxyHTTPSMu.Unlock()
  172. if proxyHTTPSRunning && proxyHTTPSServer != nil {
  173. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  174. _ = proxyHTTPSServer.Shutdown(ctx)
  175. cancel()
  176. proxyHTTPSRunning = false
  177. proxyHTTPSServer = nil
  178. }
  179. // If www-ssl service is running, port 443 is already served by it
  180. sslSvc, ok := servicesMgr.GetService("www-ssl")
  181. if ok && !sslSvc.Disabled {
  182. return nil
  183. }
  184. cfg := proxyMgr.GetConfig()
  185. if !cfg.Enabled || cfg.HTTPSPort <= 0 {
  186. return nil
  187. }
  188. tlsConfig := &tls.Config{
  189. GetCertificate: proxyMgr.GetTLSCertificate,
  190. }
  191. listenAddr := fmt.Sprintf(":%d", cfg.HTTPSPort)
  192. newSrv := &http.Server{
  193. Addr: listenAddr,
  194. Handler: srv,
  195. TLSConfig: tlsConfig,
  196. ReadTimeout: 60 * time.Second,
  197. WriteTimeout: 60 * time.Second,
  198. IdleTimeout: 120 * time.Second,
  199. }
  200. listener, err := tls.Listen("tcp", listenAddr, tlsConfig)
  201. if err != nil {
  202. return err
  203. }
  204. proxyHTTPSServer = newSrv
  205. proxyHTTPSRunning = true
  206. go func() {
  207. log.Printf("[Proxy-HTTPS] Standalone reverse proxy HTTPS server listening on %s", listenAddr)
  208. if err := newSrv.Serve(listener); err != nil && err != http.ErrServerClosed {
  209. log.Printf("[Proxy-HTTPS] Server error: %v", err)
  210. proxyHTTPSMu.Lock()
  211. proxyHTTPSRunning = false
  212. proxyHTTPSMu.Unlock()
  213. }
  214. }()
  215. return nil
  216. }
  217. defer func() {
  218. proxyHTTPSMu.Lock()
  219. if proxyHTTPSServer != nil {
  220. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  221. _ = proxyHTTPSServer.Shutdown(ctx)
  222. cancel()
  223. }
  224. proxyHTTPSMu.Unlock()
  225. }()
  226. // HTTPS (www-ssl) Dynamic Server Listener
  227. var (
  228. httpsServer *http.Server
  229. httpsMu sync.Mutex
  230. httpsRunning bool
  231. )
  232. startOrRestartHTTPS := func(port int, disabled bool) error {
  233. httpsMu.Lock()
  234. defer httpsMu.Unlock()
  235. if httpsRunning && httpsServer != nil {
  236. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
  237. _ = httpsServer.Shutdown(ctx)
  238. cancel()
  239. httpsRunning = false
  240. httpsServer = nil
  241. }
  242. if disabled {
  243. log.Printf("[HTTPS] Web SSL (www-ssl) listener stopped")
  244. return nil
  245. }
  246. sslSvc, ok := servicesMgr.GetService("www-ssl")
  247. if !ok || sslSvc.Certificate == "" {
  248. return fmt.Errorf("HTTPS requires a configured certificate")
  249. }
  250. tlsCert, err := certMgr.GetTLSCertificate(sslSvc.Certificate)
  251. if err != nil {
  252. return fmt.Errorf("HTTPS certificate %s: %w", sslSvc.Certificate, err)
  253. }
  254. tlsConfig := &tls.Config{
  255. Certificates: []tls.Certificate{tlsCert},
  256. GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
  257. // Priority: Check if reverse proxy has a rule matching this SNI domain with a certificate
  258. if cert, err := proxyMgr.GetTLSCertificate(hello); err == nil {
  259. return cert, nil
  260. }
  261. curSvc, ok := servicesMgr.GetService("www-ssl")
  262. if ok && curSvc.Certificate != "" {
  263. if c, err := certMgr.GetTLSCertificate(curSvc.Certificate); err == nil {
  264. return &c, nil
  265. }
  266. }
  267. return &tlsCert, nil
  268. },
  269. }
  270. listenAddr := fmt.Sprintf(":%d", port)
  271. newSrv := &http.Server{
  272. Addr: listenAddr,
  273. Handler: srv,
  274. TLSConfig: tlsConfig,
  275. ReadTimeout: 60 * time.Second,
  276. WriteTimeout: 60 * time.Second,
  277. IdleTimeout: 120 * time.Second,
  278. }
  279. listener, err := tls.Listen("tcp", listenAddr, tlsConfig)
  280. if err != nil {
  281. return err
  282. }
  283. httpsServer = newSrv
  284. httpsRunning = true
  285. go func() {
  286. log.Printf("[HTTPS] Web SSL (www-ssl) server listening on %s with certificate %q", listenAddr, sslSvc.Certificate)
  287. if err := newSrv.Serve(listener); err != nil && err != http.ErrServerClosed {
  288. log.Printf("[HTTPS] Server error: %v", err)
  289. httpsMu.Lock()
  290. httpsRunning = false
  291. httpsMu.Unlock()
  292. }
  293. }()
  294. return nil
  295. }
  296. servicesMgr.RegisterHook("www-ssl", func(serviceName string, port int, disabled bool) error {
  297. err := startOrRestartHTTPS(port, disabled)
  298. _ = startOrRestartProxyHTTPS()
  299. return err
  300. })
  301. if sslSvc, ok := servicesMgr.GetService("www-ssl"); ok && !sslSvc.Disabled {
  302. if err := startOrRestartHTTPS(sslSvc.Port, sslSvc.Disabled); err != nil {
  303. log.Printf("HTTPS startup failed: %v", err)
  304. }
  305. } else {
  306. // If www-ssl is disabled, start standalone proxy HTTPS listener
  307. if err := startOrRestartProxyHTTPS(); err != nil {
  308. log.Printf("Standalone HTTPS proxy startup note: %v", err)
  309. }
  310. }
  311. defer func() {
  312. httpsMu.Lock()
  313. if httpsRunning && httpsServer != nil {
  314. _ = httpsServer.Close()
  315. }
  316. httpsMu.Unlock()
  317. }()
  318. var httpMu sync.Mutex
  319. var httpServer *http.Server
  320. host, _, _ := net.SplitHostPort(*addr)
  321. startHTTP := func(port int, disabled bool) error {
  322. httpMu.Lock()
  323. defer httpMu.Unlock()
  324. old := httpServer
  325. // If www is disabled for management, keep HTTP port 80 listening if Reverse Proxy is enabled,
  326. // so that reverse proxy rules and Let's Encrypt ACME HTTP-01 challenges continue functioning.
  327. proxyActive := (proxyMgr != nil && proxyMgr.IsEnabled())
  328. if disabled && !proxyActive {
  329. httpServer = nil
  330. if old != nil {
  331. go old.Shutdown(context.Background())
  332. }
  333. return nil
  334. }
  335. address := net.JoinHostPort(host, fmt.Sprint(port))
  336. if old != nil && old.Addr == address {
  337. return nil
  338. }
  339. listener, err := net.Listen("tcp", address)
  340. if err != nil {
  341. return err
  342. }
  343. next := &http.Server{Addr: address, Handler: srv, ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second, IdleTimeout: 120 * time.Second}
  344. httpServer = next
  345. go func() {
  346. if err := next.Serve(listener); err != nil && err != http.ErrServerClosed {
  347. log.Printf("HTTP listener failed: %v", err)
  348. }
  349. }()
  350. if old != nil {
  351. go old.Shutdown(context.Background())
  352. }
  353. return nil
  354. }
  355. servicesMgr.RegisterHook("www", func(_ string, port int, disabled bool) error { return startHTTP(port, disabled) })
  356. _, httpPortText, _ := net.SplitHostPort(*addr)
  357. var httpPort int
  358. _, _ = fmt.Sscan(httpPortText, &httpPort)
  359. httpDisabled := false
  360. explicitAddr := false
  361. flag.Visit(func(f *flag.Flag) {
  362. if f.Name == "addr" {
  363. explicitAddr = true
  364. }
  365. })
  366. if svc, ok := servicesMgr.GetService("www"); ok {
  367. httpDisabled = svc.Disabled
  368. if !explicitAddr {
  369. httpPort = svc.Port
  370. }
  371. }
  372. if err := startHTTP(httpPort, httpDisabled); err != nil {
  373. log.Fatalf("HTTP startup failed: %v", err)
  374. }
  375. // Print startup banner
  376. fmt.Println("========================================================")
  377. fmt.Println(" ⚡ DebTik Appliance Controller ⚡ ")
  378. fmt.Println("========================================================")
  379. fmt.Printf(" Web UI & API : http://0.0.0.0%s\n", *addr)
  380. fmt.Printf(" CLI Telnet Service : 0.0.0.0%s (debtik-cli / telnet)\n", telnetAddr)
  381. fmt.Printf(" CLI SSH Service : 0.0.0.0%s (ssh admin@<router-ip> -p 2222)\n", sshAddr)
  382. fmt.Printf(" Metadata Storage : %s\n", *metaPath)
  383. fmt.Printf(" Debian Config Path : %s\n", network.DebianVlansCfgPath)
  384. if ifaces, err := netMgr.ListInterfaces(); err == nil {
  385. fmt.Printf(" Discovered Links : %d interfaces\n", len(ifaces))
  386. for _, i := range ifaces {
  387. fmt.Printf(" - %-10s (%s) [%s] MTU:%d\n", i.Name, i.Type, i.State, i.MTU)
  388. }
  389. }
  390. fmt.Println("========================================================")
  391. fmt.Println(" DebTik Appliance ready. Press Ctrl+C to terminate.")
  392. // Graceful shutdown handling
  393. stopChan := make(chan os.Signal, 1)
  394. signal.Notify(stopChan, os.Interrupt, syscall.SIGTERM)
  395. <-stopChan
  396. fmt.Println("\nShutting down DebTik Controller gracefully...")
  397. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  398. defer cancel()
  399. httpMu.Lock()
  400. currentHTTP := httpServer
  401. httpMu.Unlock()
  402. if currentHTTP != nil {
  403. if err := currentHTTP.Shutdown(ctx); err != nil {
  404. log.Printf("Server shutdown error: %v", err)
  405. }
  406. }
  407. fmt.Println("DebTik Controller stopped.")
  408. }
  409. func waitListener(start func() error, running func() bool) error {
  410. result := make(chan error, 1)
  411. go func() { result <- start() }()
  412. timer := time.NewTimer(5 * time.Second)
  413. defer timer.Stop()
  414. ticker := time.NewTicker(20 * time.Millisecond)
  415. defer ticker.Stop()
  416. for {
  417. select {
  418. case err := <-result:
  419. return err
  420. case <-timer.C:
  421. return fmt.Errorf("listener did not become ready")
  422. case <-ticker.C:
  423. if running() {
  424. return nil
  425. }
  426. }
  427. }
  428. }