// read environment variables for data for http client construction func InitializeAppSensorRestClient() { RestUrl, urlExists = os.LookupEnv("APPSENSOR_REST_ENGINE_URL") RestHeaderName, nameExists = os.LookupEnv("APPSENSOR_CLIENT_APPLICATION_ID_HEADER_NAME") RestHeaderValue, valueExists = os.LookupEnv("APPSENSOR_CLIENT_APPLICATION_ID_HEADER_VALUE") ClientIp, ipExists = os.LookupEnv("APPSENSOR_CLIENT_APPLICATION_IP_ADDRESS") missingFields := make([]string, 0) if !urlExists { missingFields = append(missingFields, "APPSENSOR_REST_ENGINE_URL") } if !nameExists { glog.Info("The APPSENSOR_CLIENT_APPLICATION_ID_HEADER_NAME env var not set, using default value") RestHeaderName = "X-Appsensor-Client-Application-Name" } if !valueExists { missingFields = append(missingFields, "APPSENSOR_CLIENT_APPLICATION_ID_HEADER_VALUE") } if !ipExists { missingFields = append(missingFields, "APPSENSOR_CLIENT_APPLICATION_ID_HEADER_VALUE") } if len(missingFields) > 0 { log.Fatal("The following environment variable(s) must be populated: [ " + strings.Join(missingFields, " , ") + " ]") } glog.Info("Rest client information configured properly.") }
func evaluateInvalidVerbs(r *http.Request) { if !isValidVerb(r.Method) { glog.Info("Invalid HTTP verb seen, creating event.") go ids.AddEvent("Request", "RE2", r) } }
func evaluateUnexpectedVerbs(r *http.Request) { if config.EnableGlobalPreflightRequests && r.Method == "OPTIONS" { // using OPTIONS always allowed in preflight mode return } if staticPaths.Exists(r.URL.Path) { for _, verb := range config.Resources[r.URL.Path] { if verb == r.Method { // found matching verb .. bail return } } glog.Info("Invalid verb was found for static path, creating event.") go ids.AddEvent("Request", "RE1", r) return } for _, re := range regexPaths { if re.MatchString(r.URL.Path) { for _, verb := range config.Resources["REGEX|"+r.URL.Path] { if verb == r.Method { // found matching verb .. bail return } } glog.Info("Invalid verb was found for regex path, creating event.") go ids.AddEvent("Request", "RE1", r) return } } if config.EvaluateUnlistedResources { glog.Info("No resource listing matched, creating event.") go ids.AddEvent("Request", "RE1", r) } }
func ExpireBlocks() { glog.Info("Evaluating blocks for expiration.") var removableBlocks []string for _, element := range StoredBlocks.Flatten() { var block Block //glog.Info(element) if err := json.Unmarshal([]byte(element.(string)), &block); err != nil { panic(err) } t, err := unixToTime(block.Endtime) if err != nil { glog.Fatal(err) } if time.Now().After(t) { glog.Info("Expiring block: ", block) removableBlocks = append(removableBlocks, element.(string)) } else { glog.Infof("Not expiring block:") glog.Infof("\tnow: ", time.Now()) glog.Infof("\tt: ", t) glog.Infof("\tnow unix: ", time.Now().Unix()) glog.Infof("\tt unix: ", t.Unix()) } } if len(removableBlocks) > 0 { for _, removableBlock := range removableBlocks { StoredBlocks.Remove(removableBlock) } } }
func PopulateExpectedVerbs(verbsYamlFile *string) { //var config VerbsConfig source, err := ioutil.ReadFile(*verbsYamlFile) if err != nil { panic(err) } err = yaml.Unmarshal(source, &config) if err != nil { panic(err) } for key, _ := range config.Resources { if strings.HasPrefix(key, "REGEX|") { trimmed := key[6:len(key)] // regex glog.Info("Mapped regex route = ", trimmed) r, _ := regexp.Compile(trimmed) regexResourcePaths = append(regexResourcePaths, r) } else { // static route glog.Info("Mapped static route = ", key) staticResourcePaths.Add(key) } } glog.Info("All regex resources: ", regexResourcePaths) glog.Info("All static resources: ", staticResourcePaths) glog.Info("parsed out config ", config) }
func Block(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := connections.FindIp(r) resource := r.URL.Path shouldBlock := false for _, element := range blocks.StoredBlocks.Flatten() { var block blocks.Block if err := json.Unmarshal([]byte(element.(string)), &block); err != nil { panic(err) } if block.Applies(ip, resource, time.Now()) { shouldBlock = true glog.Info("Found a matching block - denying request: ", block) break } } if shouldBlock { // deny access w.WriteHeader(http.StatusForbidden) w.Write([]byte("Access Denied")) } else { next.ServeHTTP(w, r) } }) }
func parseFlags() (*bool, *string, *string, *string, http.Handler) { const ( defaultPort = ":8080" defaultPortUsage = "default server port, ':80', ':8080'..." defaultTarget = "http://127.0.0.1:8090" defaultTargetUsage = "default redirect url, 'http://127.0.0.1:8080'" defaultBooleanUsage = "true or false" defaultRefreshUsage = "number of seconds between block refreshes" defaultExpireUsage = "number of seconds between expiration sweeps" defaultBlocksRefreshUrl = "block refresh url, 'http://localhost:8090/api/v1.0/blocks'" defaultCertFile = "cert file (e.g. cert.pem)" defaultKeyFile = "key file (e.g. key.pem)" defaultResourceVerbsMappingFile = "resource verbs mapping file (e.g. resource-verbs-mapping.yml)" defaultResourcesFile = "resources file (e.g. resources.yml)" ) // flags port := flag.String("port", defaultPort, defaultPortUsage) url := flag.String("url", defaultTarget, defaultTargetUsage) // for running in TLS mode enableTls := flag.Bool("enable-tls", false, defaultBooleanUsage) certFile := flag.String("cert-file", "cert.pem", defaultCertFile) keyFile := flag.String("key-file", "key.pem", defaultKeyFile) // related to trend detection enableTrends := flag.Bool("enable-trend-tracking", false, defaultBooleanUsage) // related to blocking enableBlocking := flag.Bool("enable-blocking", false, defaultBooleanUsage) refreshSeconds := flag.Int("blocking-refresh-rate-seconds", 30, defaultRefreshUsage) expireSeconds := flag.Int("blocking-expire-rate-seconds", 30, defaultExpireUsage) blocksRefreshUrl := flag.String("blocking-blocks-refresh-url", "http://localhost", defaultBlocksRefreshUrl) // for redis, if used redisAddress := flag.String("redis-address", ":6379", "Address to the Redis server") maxRedisConnections := flag.Int("max-redis-connections", 10, "Max connections to Redis") // related to invalid http verbs // (instead of GET, POST, HEAD ... you receive DOG, SHOE, FOREST, etc.) enableInvalidVerbs := flag.Bool("enable-invalid-verb-checking", false, defaultBooleanUsage) // related to unexpected http verbs // requires config file // config file states this file reachable over GET or HEAD, and you get a POST enableUnexpectedVerbs := flag.Bool("enable-unexpected-verb-checking", false, defaultBooleanUsage) resourceVerbsMappingFile := flag.String("resource-verbs-mapping-file", "resource-verbs-mapping.yml", defaultResourceVerbsMappingFile) // related to unexpected resources // requires config file // config file states all resources expected in the app (think site-map) // this analysis should help detect scanners probing for non-existent resources enableUnexpectedResources := flag.Bool("enable-unexpected-resource-checking", false, defaultBooleanUsage) resourcesFile := flag.String("resources-file", "resources.yml", defaultResourcesFile) flag.Parse() glog.Info("------------------------------------------------") glog.Infof("Settings:") glog.Infof("\tServer port %s", *port) glog.Infof("\tProxy url: %s", *url) glog.Infof("\tEnable TLS: %t", *enableTls) glog.Infof("\t\tCert File: %s", *certFile) glog.Infof("\t\tKey File: %s", *keyFile) glog.Infof("\tEnable trend tracking: %t", *enableTrends) glog.Infof("\tEnable blocking: %t", *enableBlocking) glog.Infof("\t\tRefresh rate (seconds): %d", *refreshSeconds) glog.Infof("\t\tExpire rate (seconds): %d", *expireSeconds) glog.Infof("\t\tBlock refresh url: %s", *blocksRefreshUrl) glog.Infof("\tEnable invalid verbs: %t", *enableInvalidVerbs) glog.Infof("\tEnable unexpected verbs: %t", *enableUnexpectedVerbs) glog.Infof("\t\tResource verbs mapping file: %s", *resourceVerbsMappingFile) glog.Infof("\tEnable unexpected resources: %t", *enableUnexpectedResources) glog.Infof("\t\tResources file: %s", *resourcesFile) glog.Info("------------------------------------------------") // proxy proxy := New(*url) proxyHandler := http.HandlerFunc(proxy.handle) // chain default handlers (clear context, recovery, and log execution time) chain := alice.New( context.ClearHandler, middleware.Recovery, middleware.LogExecutionTime) // if blocking is enabled, add it to alice chain if *enableBlocking { chain = chain.Append(middleware.Block) go doEvery((time.Duration(*refreshSeconds) * time.Second), func() { blocks.RefreshBlocks(blocksRefreshUrl) }) go doEvery((time.Duration(*expireSeconds) * time.Second), blocks.ExpireBlocks) } // if trending is enabled, add it to alice chain if *enableTrends { chain = chain.Append(middleware.Trend) } // if invalid verb checking is enabled, add it to alice chain if *enableInvalidVerbs { chain = chain.Append(middleware.InvalidVerbs) } // if unexpected verb checking is enabled, add it to alice chain if *enableUnexpectedVerbs { middleware.PopulateExpectedVerbs(resourceVerbsMappingFile) chain = chain.Append(middleware.UnexpectedVerbs) } // if unexpected resource checking is enabled, add it to alice chain if *enableUnexpectedResources { middleware.PopulateExpectedResources(resourcesFile) chain = chain.Append(middleware.UnexpectedResources) } // if any of these settings are enabled, connect to redis if *enableTrends { connections.ConnectRedis(redisAddress, maxRedisConnections) } // if any of these settings are enabled, setup rest client to appsensor backend if *enableInvalidVerbs || *enableTrends || *enableUnexpectedVerbs || *enableUnexpectedResources { ids.InitializeAppSensorRestClient() } // always use reverse proxy as chain target (final) chainHandler := chain.Then(proxyHandler) return enableTls, port, certFile, keyFile, chainHandler }
func (p *Prox) handle(w http.ResponseWriter, r *http.Request) { glog.Info("saw a request for ", r.URL) p.proxy.ServeHTTP(w, r) }