github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/server.go (about) 1 package gateway 2 3 import ( 4 "context" 5 "html/template" 6 "io/ioutil" 7 stdlog "log" 8 "log/syslog" 9 "net" 10 "net/http" 11 pprof_http "net/http/pprof" 12 "os" 13 "path/filepath" 14 "runtime" 15 "runtime/pprof" 16 "strconv" 17 "strings" 18 "sync" 19 textTemplate "text/template" 20 "time" 21 22 logstashHook "github.com/bshuster-repo/logrus-logstash-hook" 23 "github.com/evalphobia/logrus_sentry" 24 graylogHook "github.com/gemnasium/logrus-graylog-hook" 25 "github.com/gorilla/mux" 26 "github.com/justinas/alice" 27 "github.com/lonelycode/osin" 28 newrelic "github.com/newrelic/go-agent" 29 "github.com/rs/cors" 30 uuid "github.com/satori/go.uuid" 31 "github.com/sirupsen/logrus" 32 logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" 33 "rsc.io/letsencrypt" 34 35 "github.com/TykTechnologies/again" 36 gas "github.com/TykTechnologies/goautosocket" 37 "github.com/TykTechnologies/gorpc" 38 "github.com/TykTechnologies/tyk/apidef" 39 "github.com/TykTechnologies/tyk/certs" 40 "github.com/TykTechnologies/tyk/checkup" 41 "github.com/TykTechnologies/tyk/cli" 42 "github.com/TykTechnologies/tyk/config" 43 "github.com/TykTechnologies/tyk/dnscache" 44 "github.com/TykTechnologies/tyk/headers" 45 logger "github.com/TykTechnologies/tyk/log" 46 "github.com/TykTechnologies/tyk/regexp" 47 "github.com/TykTechnologies/tyk/rpc" 48 "github.com/TykTechnologies/tyk/storage" 49 "github.com/TykTechnologies/tyk/trace" 50 "github.com/TykTechnologies/tyk/user" 51 ) 52 53 var ( 54 log = logger.Get() 55 mainLog = log.WithField("prefix", "main") 56 pubSubLog = log.WithField("prefix", "pub-sub") 57 rawLog = logger.GetRaw() 58 templates *template.Template 59 templatesRaw *textTemplate.Template 60 analytics RedisAnalyticsHandler 61 GlobalEventsJSVM JSVM 62 memProfFile *os.File 63 MainNotifier RedisNotifier 64 DefaultOrgStore DefaultSessionManager 65 DefaultQuotaStore DefaultSessionManager 66 FallbackKeySesionManager = SessionHandler(&DefaultSessionManager{}) 67 MonitoringHandler config.TykEventHandler 68 RPCListener RPCStorageHandler 69 DashService DashboardServiceSender 70 CertificateManager *certs.CertificateManager 71 NewRelicApplication newrelic.Application 72 73 apisMu sync.RWMutex 74 apiSpecs []*APISpec 75 apisByID = map[string]*APISpec{} 76 apisHandlesByID = new(sync.Map) 77 78 keyGen DefaultKeyGenerator 79 80 policiesMu sync.RWMutex 81 policiesByID = map[string]user.Policy{} 82 83 LE_MANAGER letsencrypt.Manager 84 LE_FIRSTRUN bool 85 86 muNodeID sync.Mutex // guards NodeID 87 NodeID string 88 89 runningTestsMu sync.RWMutex 90 testMode bool 91 92 // confPaths is the series of paths to try to use as config files. The 93 // first one to exist will be used. If none exists, a default config 94 // will be written to the first path in the list. 95 // 96 // When --conf=foo is used, this will be replaced by []string{"foo"}. 97 confPaths = []string{ 98 "tyk.conf", 99 // TODO: add ~/.config/tyk/tyk.conf here? 100 "/etc/tyk/tyk.conf", 101 } 102 103 dnsCacheManager dnscache.IDnsCacheManager 104 ) 105 106 const ( 107 defReadTimeout = 120 * time.Second 108 defWriteTimeout = 120 * time.Second 109 appName = "tyk-gateway" 110 ) 111 112 // SetNodeID writes NodeID safely. 113 func SetNodeID(nodeID string) { 114 muNodeID.Lock() 115 NodeID = nodeID 116 muNodeID.Unlock() 117 } 118 119 // GetNodeID reads NodeID safely. 120 func GetNodeID() string { 121 muNodeID.Lock() 122 defer muNodeID.Unlock() 123 return NodeID 124 } 125 126 func isRunningTests() bool { 127 runningTestsMu.RLock() 128 v := testMode 129 runningTestsMu.RUnlock() 130 return v 131 } 132 133 func setTestMode(v bool) { 134 runningTestsMu.Lock() 135 testMode = v 136 runningTestsMu.Unlock() 137 } 138 139 func getApiSpec(apiID string) *APISpec { 140 apisMu.RLock() 141 spec := apisByID[apiID] 142 apisMu.RUnlock() 143 return spec 144 } 145 146 func apisByIDLen() int { 147 apisMu.RLock() 148 defer apisMu.RUnlock() 149 return len(apisByID) 150 } 151 152 var redisPurgeOnce sync.Once 153 var rpcPurgeOnce sync.Once 154 var purgeTicker = time.Tick(time.Second) 155 var rpcPurgeTicker = time.Tick(10 * time.Second) 156 157 // Create all globals and init connection handlers 158 func setupGlobals(ctx context.Context) { 159 160 reloadMu.Lock() 161 defer reloadMu.Unlock() 162 163 dnsCacheManager = dnscache.NewDnsCacheManager(config.Global().DnsCache.MultipleIPsHandleStrategy) 164 if config.Global().DnsCache.Enabled { 165 dnsCacheManager.InitDNSCaching( 166 time.Duration(config.Global().DnsCache.TTL)*time.Second, 167 time.Duration(config.Global().DnsCache.CheckInterval)*time.Second) 168 } 169 170 if config.Global().EnableAnalytics && config.Global().Storage.Type != "redis" { 171 mainLog.Fatal("Analytics requires Redis Storage backend, please enable Redis in the tyk.conf file.") 172 } 173 174 // Initialise HostCheckerManager only if uptime tests are enabled. 175 if !config.Global().UptimeTests.Disable { 176 if config.Global().ManagementNode { 177 mainLog.Warn("Running Uptime checks in a management node.") 178 } 179 healthCheckStore := storage.RedisCluster{KeyPrefix: "host-checker:"} 180 InitHostCheckManager(&healthCheckStore) 181 } 182 183 redisStore := storage.RedisCluster{KeyPrefix: "apikey-", HashKeys: config.Global().HashKeys} 184 FallbackKeySesionManager.Init(&redisStore) 185 186 if config.Global().EnableAnalytics && analytics.Store == nil { 187 globalConf := config.Global() 188 globalConf.LoadIgnoredIPs() 189 config.SetGlobal(globalConf) 190 mainLog.Debug("Setting up analytics DB connection") 191 192 analyticsStore := storage.RedisCluster{KeyPrefix: "analytics-"} 193 analytics.Store = &analyticsStore 194 analytics.Init(globalConf) 195 196 redisPurgeOnce.Do(func() { 197 store := storage.RedisCluster{KeyPrefix: "analytics-"} 198 redisPurger := RedisPurger{Store: &store} 199 go redisPurger.PurgeLoop(purgeTicker) 200 }) 201 202 if config.Global().AnalyticsConfig.Type == "rpc" { 203 mainLog.Debug("Using RPC cache purge") 204 205 rpcPurgeOnce.Do(func() { 206 store := storage.RedisCluster{KeyPrefix: "analytics-"} 207 purger := rpc.Purger{ 208 Store: &store, 209 } 210 purger.Connect() 211 go purger.PurgeLoop(rpcPurgeTicker) 212 }) 213 } 214 go flushNetworkAnalytics(ctx) 215 } 216 217 // Load all the files that have the "error" prefix. 218 templatesDir := filepath.Join(config.Global().TemplatePath, "error*") 219 templates = template.Must(template.ParseGlob(templatesDir)) 220 templatesRaw = textTemplate.Must(textTemplate.ParseGlob(templatesDir)) 221 222 CoProcessInit() 223 224 // Get the notifier ready 225 mainLog.Debug("Notifier will not work in hybrid mode") 226 mainNotifierStore := &storage.RedisCluster{} 227 mainNotifierStore.Connect() 228 MainNotifier = RedisNotifier{mainNotifierStore, RedisPubSubChannel} 229 230 if config.Global().Monitor.EnableTriggerMonitors { 231 h := &WebHookHandler{} 232 if err := h.Init(config.Global().Monitor.Config); err != nil { 233 mainLog.Error("Failed to initialise monitor! ", err) 234 } else { 235 MonitoringHandler = h 236 } 237 } 238 239 if globalConfig := config.Global(); globalConfig.AnalyticsConfig.NormaliseUrls.Enabled { 240 mainLog.Info("Setting up analytics normaliser") 241 globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet = initNormalisationPatterns() 242 config.SetGlobal(globalConfig) 243 } 244 245 certificateSecret := config.Global().Secret 246 if config.Global().Security.PrivateCertificateEncodingSecret != "" { 247 certificateSecret = config.Global().Security.PrivateCertificateEncodingSecret 248 } 249 250 CertificateManager = certs.NewCertificateManager(getGlobalStorageHandler("cert-", false), certificateSecret, log) 251 252 if config.Global().NewRelic.AppName != "" { 253 NewRelicApplication = SetupNewRelic() 254 } 255 } 256 257 func buildConnStr(resource string) string { 258 259 if config.Global().DBAppConfOptions.ConnectionString == "" && config.Global().DisableDashboardZeroConf { 260 mainLog.Fatal("Connection string is empty, failing.") 261 } 262 263 if !config.Global().DisableDashboardZeroConf && config.Global().DBAppConfOptions.ConnectionString == "" { 264 mainLog.Info("Waiting for zeroconf signal...") 265 for config.Global().DBAppConfOptions.ConnectionString == "" { 266 time.Sleep(1 * time.Second) 267 } 268 } 269 270 return config.Global().DBAppConfOptions.ConnectionString + resource 271 } 272 273 func syncAPISpecs() (int, error) { 274 loader := APIDefinitionLoader{} 275 apisMu.Lock() 276 defer apisMu.Unlock() 277 var s []*APISpec 278 if config.Global().UseDBAppConfigs { 279 connStr := buildConnStr("/system/apis") 280 tmpSpecs, err := loader.FromDashboardService(connStr, config.Global().NodeSecret) 281 if err != nil { 282 log.Error("failed to load API specs: ", err) 283 return 0, err 284 } 285 286 s = tmpSpecs 287 288 mainLog.Debug("Downloading API Configurations from Dashboard Service") 289 } else if config.Global().SlaveOptions.UseRPC { 290 mainLog.Debug("Using RPC Configuration") 291 292 var err error 293 s, err = loader.FromRPC(config.Global().SlaveOptions.RPCKey) 294 if err != nil { 295 return 0, err 296 } 297 } else { 298 s = loader.FromDir(config.Global().AppPath) 299 } 300 301 mainLog.Printf("Detected %v APIs", len(s)) 302 303 if config.Global().AuthOverride.ForceAuthProvider { 304 for i := range s { 305 s[i].AuthProvider = config.Global().AuthOverride.AuthProvider 306 } 307 } 308 309 if config.Global().AuthOverride.ForceSessionProvider { 310 for i := range s { 311 s[i].SessionProvider = config.Global().AuthOverride.SessionProvider 312 } 313 } 314 var filter []*APISpec 315 for _, v := range s { 316 if err := v.Validate(); err != nil { 317 mainLog.Infof("Skipping loading spec:%q because it failed validation with error:%v", v.Name, err) 318 continue 319 } 320 filter = append(filter, v) 321 } 322 apiSpecs = filter 323 324 tlsConfigCache.Flush() 325 326 return len(apiSpecs), nil 327 } 328 329 func syncPolicies() (count int, err error) { 330 var pols map[string]user.Policy 331 332 mainLog.Info("Loading policies") 333 334 switch config.Global().Policies.PolicySource { 335 case "service": 336 if config.Global().Policies.PolicyConnectionString == "" { 337 mainLog.Fatal("No connection string or node ID present. Failing.") 338 } 339 connStr := config.Global().Policies.PolicyConnectionString 340 connStr = connStr + "/system/policies" 341 342 mainLog.Info("Using Policies from Dashboard Service") 343 344 pols = LoadPoliciesFromDashboard(connStr, config.Global().NodeSecret, config.Global().Policies.AllowExplicitPolicyID) 345 case "rpc": 346 mainLog.Debug("Using Policies from RPC") 347 pols, err = LoadPoliciesFromRPC(config.Global().SlaveOptions.RPCKey) 348 default: 349 // this is the only case now where we need a policy record name 350 if config.Global().Policies.PolicyRecordName == "" { 351 mainLog.Debug("No policy record name defined, skipping...") 352 return 0, nil 353 } 354 pols = LoadPoliciesFromFile(config.Global().Policies.PolicyRecordName) 355 } 356 mainLog.Infof("Policies found (%d total):", len(pols)) 357 for id := range pols { 358 mainLog.Debugf(" - %s", id) 359 } 360 361 policiesMu.Lock() 362 defer policiesMu.Unlock() 363 if len(pols) > 0 { 364 policiesByID = pols 365 } 366 367 return len(pols), err 368 } 369 370 // stripSlashes removes any trailing slashes from the request's URL 371 // path. 372 func stripSlashes(next http.Handler) http.Handler { 373 fn := func(w http.ResponseWriter, r *http.Request) { 374 path := r.URL.Path 375 if trim := strings.TrimRight(path, "/"); trim != path { 376 r2 := *r 377 r2.URL.Path = trim 378 r = &r2 379 } 380 next.ServeHTTP(w, r) 381 } 382 return http.HandlerFunc(fn) 383 } 384 385 func controlAPICheckClientCertificate(certLevel string, next http.Handler) http.Handler { 386 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 387 if config.Global().Security.ControlAPIUseMutualTLS { 388 if err := CertificateManager.ValidateRequestCertificate(config.Global().Security.Certificates.ControlAPI, r); err != nil { 389 doJSONWrite(w, http.StatusForbidden, apiError(err.Error())) 390 return 391 } 392 } 393 394 next.ServeHTTP(w, r) 395 }) 396 } 397 398 func loadAPIEndpoints(muxer *mux.Router) { 399 hostname := config.Global().HostName 400 if config.Global().ControlAPIHostname != "" { 401 hostname = config.Global().ControlAPIHostname 402 } 403 404 if muxer == nil { 405 cp := config.Global().ControlAPIPort 406 muxer = defaultProxyMux.router(cp, "") 407 if muxer == nil { 408 if cp != 0 { 409 log.Error("Can't find control API router") 410 } 411 return 412 } 413 } 414 415 r := mux.NewRouter() 416 muxer.PathPrefix("/tyk/").Handler(http.StripPrefix("/tyk", 417 stripSlashes(checkIsAPIOwner(controlAPICheckClientCertificate("/gateway/client", InstrumentationMW(r)))), 418 )) 419 420 if hostname != "" { 421 muxer = muxer.Host(hostname).Subrouter() 422 mainLog.Info("Control API hostname set: ", hostname) 423 } 424 425 if *cli.HTTPProfile || config.Global().HTTPProfile { 426 muxer.HandleFunc("/debug/pprof/profile", pprof_http.Profile) 427 muxer.HandleFunc("/debug/pprof/{_:.*}", pprof_http.Index) 428 } 429 430 r.MethodNotAllowedHandler = MethodNotAllowedHandler{} 431 432 mainLog.Info("Initialising Tyk REST API Endpoints") 433 434 // set up main API handlers 435 r.HandleFunc("/reload/group", groupResetHandler).Methods("GET") 436 r.HandleFunc("/reload", resetHandler(nil)).Methods("GET") 437 438 if !isRPCMode() { 439 r.HandleFunc("/org/keys", orgHandler).Methods("GET") 440 r.HandleFunc("/org/keys/{keyName:[^/]*}", orgHandler).Methods("POST", "PUT", "GET", "DELETE") 441 r.HandleFunc("/keys/policy/{keyName}", policyUpdateHandler).Methods("POST") 442 r.HandleFunc("/keys/create", createKeyHandler).Methods("POST") 443 r.HandleFunc("/apis", apiHandler).Methods("GET", "POST", "PUT", "DELETE") 444 r.HandleFunc("/apis/{apiID}", apiHandler).Methods("GET", "POST", "PUT", "DELETE") 445 r.HandleFunc("/health", healthCheckhandler).Methods("GET") 446 r.HandleFunc("/oauth/clients/create", createOauthClient).Methods("POST") 447 r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}", oAuthClientHandler).Methods("PUT") 448 r.HandleFunc("/oauth/clients/apis/{appID}", getApisForOauthApp).Queries("orgID", "{[0-9]*?}").Methods("GET") 449 r.HandleFunc("/oauth/refresh/{keyName}", invalidateOauthRefresh).Methods("DELETE") 450 r.HandleFunc("/cache/{apiID}", invalidateCacheHandler).Methods("DELETE") 451 r.HandleFunc("/oauth/revoke", RevokeTokenHandler).Methods("POST") 452 r.HandleFunc("/oauth/revoke_all", RevokeAllTokensHandler).Methods("POST") 453 454 } else { 455 mainLog.Info("Node is slaved, REST API minimised") 456 } 457 458 r.HandleFunc("/debug", traceHandler).Methods("POST") 459 460 r.HandleFunc("/keys", keyHandler).Methods("POST", "PUT", "GET", "DELETE") 461 r.HandleFunc("/keys/preview", previewKeyHandler).Methods("POST") 462 r.HandleFunc("/keys/{keyName:[^/]*}", keyHandler).Methods("POST", "PUT", "GET", "DELETE") 463 r.HandleFunc("/certs", certHandler).Methods("POST", "GET") 464 r.HandleFunc("/certs/{certID:[^/]*}", certHandler).Methods("POST", "GET", "DELETE") 465 r.HandleFunc("/oauth/clients/{apiID}", oAuthClientHandler).Methods("GET", "DELETE") 466 r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}", oAuthClientHandler).Methods("GET", "DELETE") 467 r.HandleFunc("/oauth/clients/{apiID}/{keyName}/tokens", oAuthClientTokensHandler).Methods("GET") 468 469 mainLog.Debug("Loaded API Endpoints") 470 } 471 472 // checkIsAPIOwner will ensure that the accessor of the tyk API has the 473 // correct security credentials - this is a shared secret between the 474 // client and the owner and is set in the tyk.conf file. This should 475 // never be made public! 476 func checkIsAPIOwner(next http.Handler) http.Handler { 477 secret := config.Global().Secret 478 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 479 tykAuthKey := r.Header.Get(headers.XTykAuthorization) 480 if tykAuthKey != secret { 481 // Error 482 mainLog.Warning("Attempted administrative access with invalid or missing key!") 483 484 doJSONWrite(w, http.StatusForbidden, apiError("Attempted administrative access with invalid or missing key!")) 485 return 486 } 487 next.ServeHTTP(w, r) 488 }) 489 } 490 491 func generateOAuthPrefix(apiID string) string { 492 return "oauth-data." + apiID + "." 493 } 494 495 // Create API-specific OAuth handlers and respective auth servers 496 func addOAuthHandlers(spec *APISpec, muxer *mux.Router) *OAuthManager { 497 var pathSeparator string 498 if !strings.HasSuffix(spec.Proxy.ListenPath, "/") { 499 pathSeparator = "/" 500 } 501 502 apiAuthorizePath := spec.Proxy.ListenPath + pathSeparator + "tyk/oauth/authorize-client{_:/?}" 503 clientAuthPath := spec.Proxy.ListenPath + pathSeparator + "oauth/authorize{_:/?}" 504 clientAccessPath := spec.Proxy.ListenPath + pathSeparator + "oauth/token{_:/?}" 505 revokeToken := spec.Proxy.ListenPath + pathSeparator + "oauth/revoke" 506 revokeAllTokens := spec.Proxy.ListenPath + pathSeparator + "oauth/revoke_all" 507 508 serverConfig := osin.NewServerConfig() 509 510 if config.Global().OauthErrorStatusCode != 0 { 511 serverConfig.ErrorStatusCode = config.Global().OauthErrorStatusCode 512 } else { 513 serverConfig.ErrorStatusCode = http.StatusForbidden 514 } 515 516 serverConfig.AllowedAccessTypes = spec.Oauth2Meta.AllowedAccessTypes 517 serverConfig.AllowedAuthorizeTypes = spec.Oauth2Meta.AllowedAuthorizeTypes 518 serverConfig.RedirectUriSeparator = config.Global().OauthRedirectUriSeparator 519 520 prefix := generateOAuthPrefix(spec.APIID) 521 storageManager := getGlobalStorageHandler(prefix, false) 522 storageManager.Connect() 523 osinStorage := &RedisOsinStorageInterface{storageManager, spec.SessionManager, &storage.RedisCluster{KeyPrefix: prefix, HashKeys: false}, spec.OrgID} 524 osinServer := TykOsinNewServer(serverConfig, osinStorage) 525 526 oauthManager := OAuthManager{spec, osinServer} 527 oauthHandlers := OAuthHandlers{oauthManager} 528 529 muxer.Handle(apiAuthorizePath, checkIsAPIOwner(allowMethods(oauthHandlers.HandleGenerateAuthCodeData, "POST"))) 530 muxer.HandleFunc(clientAuthPath, allowMethods(oauthHandlers.HandleAuthorizePassthrough, "GET", "POST")) 531 muxer.HandleFunc(clientAccessPath, addSecureAndCacheHeaders(allowMethods(oauthHandlers.HandleAccessRequest, "GET", "POST"))) 532 muxer.HandleFunc(revokeToken, oauthHandlers.HandleRevokeToken) 533 muxer.HandleFunc(revokeAllTokens, oauthHandlers.HandleRevokeAllTokens) 534 return &oauthManager 535 } 536 537 func addBatchEndpoint(spec *APISpec, muxer *mux.Router) { 538 mainLog.Debug("Batch requests enabled for API") 539 apiBatchPath := spec.Proxy.ListenPath + "tyk/batch/" 540 batchHandler := BatchRequestHandler{API: spec} 541 muxer.HandleFunc(apiBatchPath, batchHandler.HandleBatchRequest) 542 } 543 544 func loadCustomMiddleware(spec *APISpec) ([]string, apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, apidef.MiddlewareDriver) { 545 mwPaths := []string{} 546 var mwAuthCheckFunc apidef.MiddlewareDefinition 547 mwPreFuncs := []apidef.MiddlewareDefinition{} 548 mwPostFuncs := []apidef.MiddlewareDefinition{} 549 mwPostKeyAuthFuncs := []apidef.MiddlewareDefinition{} 550 mwDriver := apidef.OttoDriver 551 552 // Set AuthCheck hook 553 if spec.CustomMiddleware.AuthCheck.Name != "" { 554 mwAuthCheckFunc = spec.CustomMiddleware.AuthCheck 555 if spec.CustomMiddleware.AuthCheck.Path != "" { 556 // Feed a JS file to Otto 557 mwPaths = append(mwPaths, spec.CustomMiddleware.AuthCheck.Path) 558 } 559 } 560 561 // Load from the configuration 562 for _, mwObj := range spec.CustomMiddleware.Pre { 563 mwPaths = append(mwPaths, mwObj.Path) 564 mwPreFuncs = append(mwPreFuncs, mwObj) 565 mainLog.Debug("Loading custom PRE-PROCESSOR middleware: ", mwObj.Name) 566 } 567 for _, mwObj := range spec.CustomMiddleware.Post { 568 mwPaths = append(mwPaths, mwObj.Path) 569 mwPostFuncs = append(mwPostFuncs, mwObj) 570 mainLog.Debug("Loading custom POST-PROCESSOR middleware: ", mwObj.Name) 571 } 572 573 // Load from folders 574 for _, folder := range [...]struct { 575 name string 576 single *apidef.MiddlewareDefinition 577 slice *[]apidef.MiddlewareDefinition 578 }{ 579 {name: "pre", slice: &mwPreFuncs}, 580 {name: "auth", single: &mwAuthCheckFunc}, 581 {name: "post_auth", slice: &mwPostKeyAuthFuncs}, 582 {name: "post", slice: &mwPostFuncs}, 583 } { 584 globPath := filepath.Join(config.Global().MiddlewarePath, spec.APIID, folder.name, "*.js") 585 paths, _ := filepath.Glob(globPath) 586 for _, path := range paths { 587 mainLog.Debug("Loading file middleware from ", path) 588 589 mwDef := apidef.MiddlewareDefinition{ 590 Name: strings.Split(filepath.Base(path), ".")[0], 591 Path: path, 592 } 593 mainLog.Debug("-- Middleware name ", mwDef.Name) 594 mwDef.RequireSession = strings.HasSuffix(mwDef.Name, "_with_session") 595 if mwDef.RequireSession { 596 switch folder.name { 597 case "post_auth", "post": 598 mainLog.Debug("-- Middleware requires session") 599 default: 600 mainLog.Warning("Middleware requires session, but isn't post-auth: ", mwDef.Name) 601 } 602 } 603 mwPaths = append(mwPaths, path) 604 if folder.single != nil { 605 *folder.single = mwDef 606 } else { 607 *folder.slice = append(*folder.slice, mwDef) 608 } 609 } 610 } 611 612 // Set middleware driver, defaults to OttoDriver 613 if spec.CustomMiddleware.Driver != "" { 614 mwDriver = spec.CustomMiddleware.Driver 615 } 616 617 // Load PostAuthCheck hooks 618 for _, mwObj := range spec.CustomMiddleware.PostKeyAuth { 619 if mwObj.Path != "" { 620 // Otto files are specified here 621 mwPaths = append(mwPaths, mwObj.Path) 622 } 623 mwPostKeyAuthFuncs = append(mwPostKeyAuthFuncs, mwObj) 624 } 625 626 return mwPaths, mwAuthCheckFunc, mwPreFuncs, mwPostFuncs, mwPostKeyAuthFuncs, mwDriver 627 } 628 629 func createResponseMiddlewareChain(spec *APISpec) { 630 // Create the response processors 631 632 responseChain := make([]TykResponseHandler, len(spec.ResponseProcessors)) 633 for i, processorDetail := range spec.ResponseProcessors { 634 processor := responseProcessorByName(processorDetail.Name) 635 if processor == nil { 636 mainLog.Error("No such processor: ", processorDetail.Name) 637 return 638 } 639 if err := processor.Init(processorDetail.Options, spec); err != nil { 640 mainLog.Debug("Failed to init processor: ", err) 641 } 642 mainLog.Debug("Loading Response processor: ", processorDetail.Name) 643 responseChain[i] = processor 644 } 645 spec.ResponseChain = responseChain 646 } 647 648 func handleCORS(chain *[]alice.Constructor, spec *APISpec) { 649 650 if spec.CORS.Enable { 651 mainLog.Debug("CORS ENABLED") 652 c := cors.New(cors.Options{ 653 AllowedOrigins: spec.CORS.AllowedOrigins, 654 AllowedMethods: spec.CORS.AllowedMethods, 655 AllowedHeaders: spec.CORS.AllowedHeaders, 656 ExposedHeaders: spec.CORS.ExposedHeaders, 657 AllowCredentials: spec.CORS.AllowCredentials, 658 MaxAge: spec.CORS.MaxAge, 659 OptionsPassthrough: spec.CORS.OptionsPassthrough, 660 Debug: spec.CORS.Debug, 661 }) 662 663 *chain = append(*chain, c.Handler) 664 } 665 } 666 667 func isRPCMode() bool { 668 return config.Global().AuthOverride.ForceAuthProvider && 669 config.Global().AuthOverride.AuthProvider.StorageEngine == RPCStorageEngine 670 } 671 672 func rpcReloadLoop(rpcKey string) { 673 for { 674 RPCListener.CheckForReload(rpcKey) 675 } 676 } 677 678 var reloadMu sync.Mutex 679 680 func DoReload() { 681 reloadMu.Lock() 682 defer reloadMu.Unlock() 683 684 // Initialize/reset the JSVM 685 if config.Global().EnableJSVM { 686 GlobalEventsJSVM.Init(nil, logrus.NewEntry(log)) 687 } 688 689 // Load the API Policies 690 if _, err := syncPolicies(); err != nil { 691 mainLog.Error("Error during syncing policies:", err.Error()) 692 return 693 } 694 695 // load the specs 696 if count, err := syncAPISpecs(); err != nil { 697 mainLog.Error("Error during syncing apis:", err.Error()) 698 return 699 } else { 700 // skip re-loading only if dashboard service reported 0 APIs 701 // and current registry had 0 APIs 702 if count == 0 && apisByIDLen() == 0 { 703 mainLog.Warning("No API Definitions found, not reloading") 704 return 705 } 706 } 707 loadGlobalApps() 708 709 mainLog.Info("API reload complete") 710 } 711 712 // shouldReload returns true if we should perform any reload. Reloads happens if 713 // we have reload callback queued. 714 func shouldReload() ([]func(), bool) { 715 requeueLock.Lock() 716 defer requeueLock.Unlock() 717 if len(requeue) == 0 { 718 return nil, false 719 } 720 n := requeue 721 requeue = []func(){} 722 return n, true 723 } 724 725 func reloadLoop(ctx context.Context, tick <-chan time.Time, complete ...func()) { 726 for { 727 select { 728 case <-ctx.Done(): 729 return 730 // We don't check for reload right away as the gateway peroms this on the 731 // startup sequence. We expect to start checking on the first tick after the 732 // gateway is up and running. 733 case <-tick: 734 cb, ok := shouldReload() 735 if !ok { 736 continue 737 } 738 start := time.Now() 739 mainLog.Info("reload: initiating") 740 DoReload() 741 mainLog.Info("reload: complete") 742 mainLog.Info("Initiating coprocess reload") 743 DoCoprocessReload() 744 mainLog.Info("coprocess reload complete") 745 for _, c := range cb { 746 // most of the callbacks are nil, we don't want to execute nil functions to 747 // avoid panics. 748 if c != nil { 749 c() 750 } 751 } 752 if len(complete) != 0 { 753 complete[0]() 754 } 755 mainLog.Infof("reload: cycle completed in %v", time.Since(start)) 756 } 757 } 758 } 759 760 // reloadQueue is used by reloadURLStructure to queue a reload. It's not 761 // buffered, as reloadQueueLoop should pick these up immediately. 762 var reloadQueue = make(chan func()) 763 764 var requeueLock sync.Mutex 765 766 // This is a list of callbacks to execute on the next reload. It is protected by 767 // requeueLock for concurrent use. 768 var requeue []func() 769 770 func reloadQueueLoop(ctx context.Context, cb ...func()) { 771 for { 772 select { 773 case <-ctx.Done(): 774 return 775 case fn := <-reloadQueue: 776 requeueLock.Lock() 777 requeue = append(requeue, fn) 778 requeueLock.Unlock() 779 mainLog.Info("Reload queued") 780 if len(cb) != 0 { 781 cb[0]() 782 } 783 } 784 } 785 } 786 787 // reloadURLStructure will queue an API reload. The reload will 788 // eventually create a new muxer, reload all the app configs for an 789 // instance and then replace the DefaultServeMux with the new one. This 790 // enables a reconfiguration to take place without stopping any requests 791 // from being handled. 792 // 793 // done will be called when the reload is finished. Note that if a 794 // reload is already queued, another won't be queued, but done will 795 // still be called when said queued reload is finished. 796 func reloadURLStructure(done func()) { 797 reloadQueue <- done 798 } 799 800 func setupLogger() { 801 if config.Global().UseSentry { 802 mainLog.Debug("Enabling Sentry support") 803 804 logLevel := []logrus.Level{} 805 806 if config.Global().SentryLogLevel == "" { 807 logLevel = []logrus.Level{ 808 logrus.PanicLevel, 809 logrus.FatalLevel, 810 logrus.ErrorLevel, 811 } 812 } else if config.Global().SentryLogLevel == "panic" { 813 logLevel = []logrus.Level{ 814 logrus.PanicLevel, 815 logrus.FatalLevel, 816 } 817 } 818 819 hook, err := logrus_sentry.NewSentryHook(config.Global().SentryCode, logLevel) 820 821 hook.Timeout = 0 822 823 if err == nil { 824 log.Hooks.Add(hook) 825 rawLog.Hooks.Add(hook) 826 } 827 mainLog.Debug("Sentry hook active") 828 } 829 830 if config.Global().UseSyslog { 831 mainLog.Debug("Enabling Syslog support") 832 hook, err := logrus_syslog.NewSyslogHook(config.Global().SyslogTransport, 833 config.Global().SyslogNetworkAddr, 834 syslog.LOG_INFO, "") 835 836 if err == nil { 837 log.Hooks.Add(hook) 838 rawLog.Hooks.Add(hook) 839 } 840 mainLog.Debug("Syslog hook active") 841 } 842 843 if config.Global().UseGraylog { 844 mainLog.Debug("Enabling Graylog support") 845 hook := graylogHook.NewGraylogHook(config.Global().GraylogNetworkAddr, 846 map[string]interface{}{"tyk-module": "gateway"}) 847 848 log.Hooks.Add(hook) 849 rawLog.Hooks.Add(hook) 850 851 mainLog.Debug("Graylog hook active") 852 } 853 854 if config.Global().UseLogstash { 855 mainLog.Debug("Enabling Logstash support") 856 857 var hook *logstashHook.Hook 858 var err error 859 var conn net.Conn 860 if config.Global().LogstashTransport == "udp" { 861 mainLog.Debug("Connecting to Logstash with udp") 862 hook, err = logstashHook.NewHook(config.Global().LogstashTransport, 863 config.Global().LogstashNetworkAddr, 864 appName) 865 } else { 866 mainLog.Debugf("Connecting to Logstash with %s", config.Global().LogstashTransport) 867 conn, err = gas.Dial(config.Global().LogstashTransport, config.Global().LogstashNetworkAddr) 868 if err == nil { 869 hook, err = logstashHook.NewHookWithConn(conn, appName) 870 } 871 } 872 873 if err != nil { 874 log.Errorf("Error making connection for logstash: %v", err) 875 } else { 876 log.Hooks.Add(hook) 877 rawLog.Hooks.Add(hook) 878 mainLog.Debug("Logstash hook active") 879 } 880 } 881 882 if config.Global().UseRedisLog { 883 hook := newRedisHook() 884 log.Hooks.Add(hook) 885 rawLog.Hooks.Add(hook) 886 887 mainLog.Debug("Redis log hook active") 888 } 889 } 890 891 func initialiseSystem(ctx context.Context) error { 892 if isRunningTests() && os.Getenv("TYK_LOGLEVEL") == "" { 893 // `go test` without TYK_LOGLEVEL set defaults to no log 894 // output 895 log.Level = logrus.ErrorLevel 896 log.Out = ioutil.Discard 897 gorpc.SetErrorLogger(func(string, ...interface{}) {}) 898 stdlog.SetOutput(ioutil.Discard) 899 } else if *cli.DebugMode { 900 log.Level = logrus.DebugLevel 901 mainLog.Debug("Enabling debug-level output") 902 } 903 904 if *cli.Conf != "" { 905 mainLog.Debugf("Using %s for configuration", *cli.Conf) 906 confPaths = []string{*cli.Conf} 907 } else { 908 mainLog.Debug("No configuration file defined, will try to use default (tyk.conf)") 909 } 910 911 mainLog.Infof("Tyk API Gateway %s", VERSION) 912 913 if !isRunningTests() { 914 globalConf := config.Config{} 915 if err := config.Load(confPaths, &globalConf); err != nil { 916 return err 917 } 918 if globalConf.PIDFileLocation == "" { 919 globalConf.PIDFileLocation = "/var/run/tyk/tyk-gateway.pid" 920 } 921 // It's necessary to set global conf before and after calling afterConfSetup as global conf 922 // is being used by dependencies of the even handler init and then conf is modified again. 923 config.SetGlobal(globalConf) 924 afterConfSetup(&globalConf) 925 config.SetGlobal(globalConf) 926 } 927 928 overrideTykErrors() 929 930 if os.Getenv("TYK_LOGLEVEL") == "" && !*cli.DebugMode { 931 level := strings.ToLower(config.Global().LogLevel) 932 switch level { 933 case "", "info": 934 // default, do nothing 935 case "error": 936 log.Level = logrus.ErrorLevel 937 case "warn": 938 log.Level = logrus.WarnLevel 939 case "debug": 940 log.Level = logrus.DebugLevel 941 default: 942 mainLog.Fatalf("Invalid log level %q specified in config, must be error, warn, debug or info. ", level) 943 } 944 } 945 946 if config.Global().Storage.Type != "redis" { 947 mainLog.Fatal("Redis connection details not set, please ensure that the storage type is set to Redis and that the connection parameters are correct.") 948 } 949 950 // suply rpc client globals to join it main loging and instrumentation sub systems 951 rpc.Log = log 952 rpc.Instrument = instrument 953 954 setupGlobals(ctx) 955 956 globalConf := config.Global() 957 958 if *cli.Port != "" { 959 portNum, err := strconv.Atoi(*cli.Port) 960 if err != nil { 961 mainLog.Error("Port specified in flags must be a number: ", err) 962 } else { 963 globalConf.ListenPort = portNum 964 config.SetGlobal(globalConf) 965 } 966 } 967 968 // Enable all the loggers 969 setupLogger() 970 971 mainLog.Info("PIDFile location set to: ", config.Global().PIDFileLocation) 972 973 if err := writePIDFile(); err != nil { 974 mainLog.Error("Failed to write PIDFile: ", err) 975 } 976 977 if globalConf.UseDBAppConfigs && globalConf.Policies.PolicySource != config.DefaultDashPolicySource { 978 globalConf.Policies.PolicySource = config.DefaultDashPolicySource 979 globalConf.Policies.PolicyConnectionString = globalConf.DBAppConfOptions.ConnectionString 980 if globalConf.Policies.PolicyRecordName == "" { 981 globalConf.Policies.PolicyRecordName = config.DefaultDashPolicyRecordName 982 } 983 } 984 985 getHostDetails() 986 setupInstrumentation() 987 988 if config.Global().HttpServerOptions.UseLE_SSL { 989 go StartPeriodicStateBackup(&LE_MANAGER) 990 } 991 return nil 992 } 993 994 func writePIDFile() error { 995 file := config.Global().PIDFileLocation 996 if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil { 997 return err 998 } 999 pid := strconv.Itoa(os.Getpid()) 1000 return ioutil.WriteFile(file, []byte(pid), 0600) 1001 } 1002 1003 func readPIDFromFile() (int, error) { 1004 b, err := ioutil.ReadFile(config.Global().PIDFileLocation) 1005 if err != nil { 1006 return 0, err 1007 } 1008 return strconv.Atoi(string(b)) 1009 } 1010 1011 // afterConfSetup takes care of non-sensical config values (such as zero 1012 // timeouts) and sets up a few globals that depend on the config. 1013 func afterConfSetup(conf *config.Config) { 1014 if conf.SlaveOptions.CallTimeout == 0 { 1015 conf.SlaveOptions.CallTimeout = 30 1016 } 1017 1018 if conf.SlaveOptions.PingTimeout == 0 { 1019 conf.SlaveOptions.PingTimeout = 60 1020 } 1021 1022 rpc.GlobalRPCPingTimeout = time.Second * time.Duration(conf.SlaveOptions.PingTimeout) 1023 rpc.GlobalRPCCallTimeout = time.Second * time.Duration(conf.SlaveOptions.CallTimeout) 1024 initGenericEventHandlers(conf) 1025 regexp.ResetCache(time.Second*time.Duration(conf.RegexpCacheExpire), !conf.DisableRegexpCache) 1026 1027 if conf.HealthCheckEndpointName == "" { 1028 conf.HealthCheckEndpointName = "hello" 1029 } 1030 } 1031 1032 var hostDetails struct { 1033 Hostname string 1034 PID int 1035 } 1036 1037 func getHostDetails() { 1038 var err error 1039 if hostDetails.PID, err = readPIDFromFile(); err != nil { 1040 mainLog.Error("Failed ot get host pid: ", err) 1041 } 1042 if hostDetails.Hostname, err = os.Hostname(); err != nil { 1043 mainLog.Error("Failed ot get hostname: ", err) 1044 } 1045 } 1046 1047 func getGlobalStorageHandler(keyPrefix string, hashKeys bool) storage.Handler { 1048 if config.Global().SlaveOptions.UseRPC { 1049 return &RPCStorageHandler{ 1050 KeyPrefix: keyPrefix, 1051 HashKeys: hashKeys, 1052 } 1053 } 1054 return &storage.RedisCluster{KeyPrefix: keyPrefix, HashKeys: hashKeys} 1055 } 1056 1057 func Start() { 1058 ctx, cancel := context.WithCancel(context.Background()) 1059 defer cancel() 1060 cli.Init(VERSION, confPaths) 1061 cli.Parse() 1062 // Stop gateway process if not running in "start" mode: 1063 if !cli.DefaultMode { 1064 os.Exit(0) 1065 } 1066 1067 SetNodeID("solo-" + uuid.NewV4().String()) 1068 1069 if err := initialiseSystem(ctx); err != nil { 1070 mainLog.Fatalf("Error initialising system: %v", err) 1071 } 1072 1073 if config.Global().ControlAPIPort == 0 { 1074 mainLog.Warn("The control_api_port should be changed for production") 1075 } 1076 setupPortsWhitelist() 1077 1078 onFork := func() { 1079 mainLog.Warning("PREPARING TO FORK") 1080 1081 // if controlListener != nil { 1082 // if err := controlListener.Close(); err != nil { 1083 // mainLog.Error("Control listen handler exit: ", err) 1084 // } 1085 // mainLog.Info("Control listen closed") 1086 // } 1087 1088 if config.Global().UseDBAppConfigs { 1089 mainLog.Info("Stopping heartbeat") 1090 DashService.StopBeating() 1091 mainLog.Info("Waiting to de-register") 1092 time.Sleep(10 * time.Second) 1093 1094 os.Setenv("TYK_SERVICE_NONCE", ServiceNonce) 1095 os.Setenv("TYK_SERVICE_NODEID", GetNodeID()) 1096 } 1097 } 1098 err := again.ListenFrom(&defaultProxyMux.again, onFork) 1099 if err != nil { 1100 mainLog.Errorf("Initializing again %s", err) 1101 } 1102 checkup.Run(config.Global()) 1103 if tr := config.Global().Tracer; tr.Enabled { 1104 trace.SetupTracing(tr.Name, tr.Options) 1105 trace.SetLogger(mainLog) 1106 defer trace.Close() 1107 } 1108 1109 start(ctx) 1110 1111 // Wait while Redis connection pools are ready before start serving traffic 1112 if !storage.IsConnected() { 1113 mainLog.Fatal("Redis connection pools are not ready. Exiting...") 1114 } 1115 mainLog.Info("Redis connection pools are ready") 1116 1117 if *cli.MemProfile { 1118 mainLog.Debug("Memory profiling active") 1119 var err error 1120 if memProfFile, err = os.Create("tyk.mprof"); err != nil { 1121 panic(err) 1122 } 1123 defer memProfFile.Close() 1124 } 1125 if *cli.CPUProfile { 1126 mainLog.Info("Cpu profiling active") 1127 cpuProfFile, err := os.Create("tyk.prof") 1128 if err != nil { 1129 panic(err) 1130 } 1131 pprof.StartCPUProfile(cpuProfFile) 1132 defer pprof.StopCPUProfile() 1133 } 1134 if *cli.BlockProfile { 1135 mainLog.Info("Block profiling active") 1136 runtime.SetBlockProfileRate(1) 1137 } 1138 if *cli.MutexProfile { 1139 mainLog.Info("Mutex profiling active") 1140 runtime.SetMutexProfileFraction(1) 1141 } 1142 1143 // TODO: replace goagain with something that support multiple listeners 1144 // Example: https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/ 1145 startServer() 1146 1147 if again.Child() { 1148 // This is a child process, we need to murder the parent now 1149 if err := again.Kill(); err != nil { 1150 mainLog.Fatal(err) 1151 } 1152 } 1153 again.Wait(&defaultProxyMux.again) 1154 mainLog.Info("Stop signal received.") 1155 if err := defaultProxyMux.again.Close(); err != nil { 1156 mainLog.Error("Closing listeners: ", err) 1157 } 1158 // stop analytics workers 1159 if config.Global().EnableAnalytics && analytics.Store == nil { 1160 analytics.Stop() 1161 } 1162 1163 // write pprof profiles 1164 writeProfiles() 1165 1166 if config.Global().UseDBAppConfigs { 1167 mainLog.Info("Stopping heartbeat...") 1168 DashService.StopBeating() 1169 time.Sleep(2 * time.Second) 1170 DashService.DeRegister() 1171 } 1172 1173 mainLog.Info("Terminating.") 1174 1175 time.Sleep(time.Second) 1176 } 1177 1178 func writeProfiles() { 1179 if *cli.BlockProfile { 1180 f, err := os.Create("tyk.blockprof") 1181 if err != nil { 1182 panic(err) 1183 } 1184 if err = pprof.Lookup("block").WriteTo(f, 0); err != nil { 1185 panic(err) 1186 } 1187 f.Close() 1188 } 1189 if *cli.MutexProfile { 1190 f, err := os.Create("tyk.mutexprof") 1191 if err != nil { 1192 panic(err) 1193 } 1194 if err = pprof.Lookup("mutex").WriteTo(f, 0); err != nil { 1195 panic(err) 1196 } 1197 f.Close() 1198 } 1199 } 1200 1201 func start(ctx context.Context) { 1202 // Set up a default org manager so we can traverse non-live paths 1203 if !config.Global().SupressDefaultOrgStore { 1204 mainLog.Debug("Initialising default org store") 1205 DefaultOrgStore.Init(getGlobalStorageHandler("orgkey.", false)) 1206 //DefaultQuotaStore.Init(getGlobalStorageHandler(CloudHandler, "orgkey.", false)) 1207 DefaultQuotaStore.Init(getGlobalStorageHandler("orgkey.", false)) 1208 } 1209 1210 // Start listening for reload messages 1211 if !config.Global().SuppressRedisSignalReload { 1212 go startPubSubLoop() 1213 } 1214 1215 if slaveOptions := config.Global().SlaveOptions; slaveOptions.UseRPC { 1216 mainLog.Debug("Starting RPC reload listener") 1217 RPCListener = RPCStorageHandler{ 1218 KeyPrefix: "rpc.listener.", 1219 SuppressRegister: true, 1220 } 1221 1222 RPCListener.Connect() 1223 go rpcReloadLoop(slaveOptions.RPCKey) 1224 go RPCListener.StartRPCKeepaliveWatcher() 1225 go RPCListener.StartRPCLoopCheck(slaveOptions.RPCKey) 1226 } 1227 1228 // 1s is the minimum amount of time between hot reloads. The 1229 // interval counts from the start of one reload to the next. 1230 go reloadLoop(ctx, time.Tick(time.Second)) 1231 go reloadQueueLoop(ctx) 1232 } 1233 1234 func dashboardServiceInit() { 1235 if DashService == nil { 1236 DashService = &HTTPDashboardHandler{} 1237 DashService.Init() 1238 } 1239 } 1240 1241 func handleDashboardRegistration() { 1242 if !config.Global().UseDBAppConfigs { 1243 return 1244 } 1245 1246 dashboardServiceInit() 1247 1248 // connStr := buildConnStr("/register/node") 1249 if err := DashService.Register(); err != nil { 1250 dashLog.Fatal("Registration failed: ", err) 1251 } 1252 1253 go DashService.StartBeating() 1254 } 1255 1256 var drlOnce sync.Once 1257 1258 func startDRL() { 1259 switch { 1260 case config.Global().ManagementNode: 1261 return 1262 case config.Global().EnableSentinelRateLimiter, config.Global().EnableRedisRollingLimiter: 1263 return 1264 } 1265 mainLog.Info("Initialising distributed rate limiter") 1266 setupDRL() 1267 startRateLimitNotifications() 1268 } 1269 1270 func setupPortsWhitelist() { 1271 // setup listen and control ports as whitelisted 1272 globalConf := config.Global() 1273 w := globalConf.PortWhiteList 1274 if w == nil { 1275 w = make(map[string]config.PortWhiteList) 1276 } 1277 protocol := "http" 1278 if globalConf.HttpServerOptions.UseSSL { 1279 protocol = "https" 1280 } 1281 ls := config.PortWhiteList{} 1282 if v, ok := w[protocol]; ok { 1283 ls = v 1284 } 1285 ls.Ports = append(ls.Ports, globalConf.ListenPort) 1286 if globalConf.ControlAPIPort != 0 { 1287 ls.Ports = append(ls.Ports, globalConf.ControlAPIPort) 1288 } 1289 w[protocol] = ls 1290 globalConf.PortWhiteList = w 1291 config.SetGlobal(globalConf) 1292 } 1293 1294 func startServer() { 1295 // Ensure that Control listener and default http listener running on first start 1296 muxer := &proxyMux{} 1297 1298 router := mux.NewRouter() 1299 loadAPIEndpoints(router) 1300 muxer.setRouter(config.Global().ControlAPIPort, "", router) 1301 1302 if muxer.router(config.Global().ListenPort, "") == nil { 1303 muxer.setRouter(config.Global().ListenPort, "", mux.NewRouter()) 1304 } 1305 1306 defaultProxyMux.swap(muxer) 1307 1308 // handle dashboard registration and nonces if available 1309 handleDashboardRegistration() 1310 1311 // at this point NodeID is ready to use by DRL 1312 drlOnce.Do(startDRL) 1313 1314 mainLog.Infof("Tyk Gateway started (%s)", VERSION) 1315 address := config.Global().ListenAddress 1316 if config.Global().ListenAddress == "" { 1317 address = "(open interface)" 1318 } 1319 mainLog.Info("--> Listening on address: ", address) 1320 mainLog.Info("--> Listening on port: ", config.Global().ListenPort) 1321 mainLog.Info("--> PID: ", hostDetails.PID) 1322 if !rpc.IsEmergencyMode() { 1323 DoReload() 1324 } 1325 }