main.go 10 KB

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