main.go 13 KB

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