main.go 13 KB

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