bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/apiserver/apiserver.go (about) 1 package apiserver 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "time" 10 11 "bitbucket.org/Aishee/synsec/pkg/apiserver/controllers" 12 "bitbucket.org/Aishee/synsec/pkg/csconfig" 13 "bitbucket.org/Aishee/synsec/pkg/database" 14 "bitbucket.org/Aishee/synsec/pkg/types" 15 "github.com/gin-gonic/gin" 16 "github.com/go-co-op/gocron" 17 "github.com/pkg/errors" 18 log "github.com/sirupsen/logrus" 19 "gopkg.in/tomb.v2" 20 ) 21 22 var ( 23 keyLength = 32 24 ) 25 26 type APIServer struct { 27 URL string 28 TLS *csconfig.TLSCfg 29 dbClient *database.Client 30 logFile string 31 ctx context.Context 32 controller *controllers.Controller 33 flushScheduler *gocron.Scheduler 34 router *gin.Engine 35 httpServer *http.Server 36 apic *apic 37 httpServerTomb tomb.Tomb 38 } 39 40 func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) { 41 var flushScheduler *gocron.Scheduler 42 dbClient, err := database.NewClient(config.DbConfig) 43 if err != nil { 44 return &APIServer{}, fmt.Errorf("unable to init database client: %s", err) 45 } 46 47 if config.DbConfig.Flush != nil { 48 flushScheduler, err = dbClient.StartFlushScheduler(config.DbConfig.Flush) 49 if err != nil { 50 return &APIServer{}, err 51 } 52 } 53 54 logFile := "" 55 if config.LogMedia == "file" { 56 logFile = fmt.Sprintf("%s/synsec_api.log", config.LogDir) 57 } 58 59 if log.GetLevel() < log.DebugLevel { 60 gin.SetMode(gin.ReleaseMode) 61 } 62 log.Debugf("starting router, logging to %s", logFile) 63 router := gin.New() 64 /* See https://github.com/gin-gonic/gin/pull/2474: 65 Gin does not handle safely X-Forwarded-For or X-Real-IP. 66 We do not trust them by default, but the user can opt-in 67 if they host LAPI behind a trusted proxy which sanitize 68 X-Forwarded-For and X-Real-IP. 69 */ 70 router.ForwardedByClientIP = config.UseForwardedForHeaders 71 72 /*The logger that will be used by handlers*/ 73 clog := log.New() 74 if err := types.ConfigureLogger(clog); err != nil { 75 return nil, errors.Wrap(err, "while configuring gin logger") 76 } 77 if config.LogLevel != nil { 78 clog.SetLevel(*config.LogLevel) 79 } 80 81 gin.DefaultErrorWriter = clog.Writer() 82 83 // Logging to a file. 84 if logFile != "" { 85 file, err := os.Create(logFile) 86 if err != nil { 87 return &APIServer{}, errors.Wrapf(err, "creating api access log file: %s", logFile) 88 } 89 gin.DefaultWriter = io.MultiWriter(file) 90 } 91 92 router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { 93 return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n", 94 param.ClientIP, 95 param.TimeStamp.Format(time.RFC1123), 96 param.Method, 97 param.Path, 98 param.Request.Proto, 99 param.StatusCode, 100 param.Latency, 101 param.Request.UserAgent(), 102 param.ErrorMessage, 103 ) 104 })) 105 106 router.NoRoute(func(c *gin.Context) { 107 c.JSON(http.StatusNotFound, gin.H{"message": "Page or Method not found"}) 108 return 109 }) 110 router.Use(gin.Recovery()) 111 controller := &controllers.Controller{ 112 DBClient: dbClient, 113 Ectx: context.Background(), 114 Router: router, 115 Profiles: config.Profiles, 116 Log: clog, 117 } 118 119 var apiClient *apic 120 121 if config.OnlineClient != nil && config.OnlineClient.Credentials != nil { 122 log.Printf("Loading CAPI pusher") 123 apiClient, err = NewAPIC(config.OnlineClient, dbClient) 124 if err != nil { 125 return &APIServer{}, err 126 } 127 controller.CAPIChan = apiClient.alertToPush 128 } else { 129 apiClient = nil 130 controller.CAPIChan = nil 131 } 132 133 if err := controller.Init(); err != nil { 134 return &APIServer{}, err 135 } 136 137 return &APIServer{ 138 URL: config.ListenURI, 139 TLS: config.TLS, 140 logFile: logFile, 141 dbClient: dbClient, 142 controller: controller, 143 flushScheduler: flushScheduler, 144 router: router, 145 apic: apiClient, 146 httpServerTomb: tomb.Tomb{}, 147 }, nil 148 149 } 150 151 func (s *APIServer) Router() (*gin.Engine, error) { 152 return s.router, nil 153 } 154 155 func (s *APIServer) Run() error { 156 defer types.CatchPanic("lapi/runServer") 157 158 s.httpServer = &http.Server{ 159 Addr: s.URL, 160 Handler: s.router, 161 } 162 163 if s.apic != nil { 164 s.apic.pushTomb.Go(func() error { 165 if err := s.apic.Push(); err != nil { 166 log.Errorf("capi push: %s", err) 167 return err 168 } 169 return nil 170 }) 171 s.apic.pullTomb.Go(func() error { 172 if err := s.apic.Pull(); err != nil { 173 log.Errorf("capi pull: %s", err) 174 return err 175 } 176 return nil 177 }) 178 s.apic.metricsTomb.Go(func() error { 179 if err := s.apic.SendMetrics(); err != nil { 180 log.Errorf("capi metrics: %s", err) 181 return err 182 } 183 return nil 184 }) 185 } 186 187 s.httpServerTomb.Go(func() error { 188 go func() { 189 if s.TLS != nil && s.TLS.CertFilePath != "" && s.TLS.KeyFilePath != "" { 190 if err := s.httpServer.ListenAndServeTLS(s.TLS.CertFilePath, s.TLS.KeyFilePath); err != nil { 191 log.Fatalf(err.Error()) 192 } 193 } else { 194 if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed { 195 log.Fatalf(err.Error()) 196 } 197 } 198 }() 199 <-s.httpServerTomb.Dying() 200 log.Infof("run: shutting down api server") 201 if err := s.Shutdown(); err != nil { 202 log.Errorf("while shutting down API Server : %s", err) 203 return err 204 } 205 return nil 206 }) 207 208 return nil 209 } 210 211 func (s *APIServer) Close() { 212 if s.apic != nil { 213 s.apic.Shutdown() // stop apic first since it use dbClient 214 } 215 s.dbClient.Ent.Close() 216 if s.flushScheduler != nil { 217 s.flushScheduler.Stop() 218 } 219 } 220 221 func (s *APIServer) Shutdown() error { 222 s.Close() 223 if err := s.httpServer.Shutdown(context.TODO()); err != nil { 224 return err 225 } 226 return nil 227 }