diff --git a/README.md b/README.md index 4568cf9..3cfee99 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ It is possible to add global IP allow and block lists, the helm chart will have * allowing IP addresses via `/lists/allowedips` file which is a single line per entry of ip address to allow * blocking IP addresses via `/lists/blockedips` file which is a single line per entry of ip address to block -There are also annotations that can be added to specific `Kind: Ingress` objects that allow for ip allow or blocking. +There are also annotations that can be added to the namespace, or individual `Kind: Ingress` objects that allow for ip allow or blocking. * `idling.amazee.io/ip-allow-list` - a comma separated list of ip addresses to allow, will be checked against x-forward-for, but if true-client-ip is provided it will prefer this. * `idling.amazee.io/ip-block-list` - a comma separated list of ip addresses to allow, will be checked against x-forward-for, but if true-client-ip is provided it will prefer this. @@ -40,7 +40,7 @@ It is possible to add global UserAgent allow and block lists, the helm chart wil * allowing user agents via a `/lists/allowedagents` file which is a single line per entry of useragents or regex patterns to match against. These must be `go` based regular expressions. * blocking user agents via a `/lists/blockedagents` file which is a single line per entry of useragents or regex patterns to match against. These must be `go` based regular expressions. -There are also annotations that can be added to specific `Kind: Ingress` objects that allow for user agent allow or blocking. +There are also annotations that can be added to the namespace, or individual `Kind: Ingress` objects that allow for user agent allow or blocking. * `idling.amazee.io/allowed-agents` - a comma separated list of user agents or regex patterns to allow. * `idling.amazee.io/blocked-agents` - a comma separated list of user agents or regex patterns to block. diff --git a/handlers/idler/service-kubernetes.go b/handlers/idler/service-kubernetes.go index 9195d97..dc9c933 100644 --- a/handlers/idler/service-kubernetes.go +++ b/handlers/idler/service-kubernetes.go @@ -76,8 +76,6 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n opLog.Error(err, fmt.Sprintf("Error getting deployments")) return } - // fmt.Println(labelRequirements) - // fmt.Println("deploys", len(deployments.Items)) for _, deployment := range deployments.Items { checkPods := false zeroReps := new(int32) @@ -134,11 +132,11 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n ) result, warnings, err := v1api.Query(ctx, promQuery, time.Now()) if err != nil { - fmt.Printf("Error querying Prometheus: %v\n", err) + opLog.Error(err, "Error querying Prometheus") return } if len(warnings) > 0 { - fmt.Printf("Warnings: %v\n", warnings) + opLog.Info(fmt.Sprintf("Warnings: %v", warnings)) } // and then add up the results of all the status requests to determine hit count if result.Type() == prometheusmodel.ValVector { diff --git a/handlers/unidler/handler.go b/handlers/unidler/handler.go index e37d7aa..2853c23 100644 --- a/handlers/unidler/handler.go +++ b/handlers/unidler/handler.go @@ -12,6 +12,7 @@ import ( "time" "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" networkv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/types" ) @@ -66,6 +67,13 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re // @TODO: check for code 503 specifically, or just any request that has the namespace in it will be "unidled" if a request comes in for // that ingress and the if ns != "" { + namespace := &corev1.Namespace{} + if err := h.Client.Get(ctx, types.NamespacedName{ + Name: ns, + }, namespace); err != nil { + opLog.Info(fmt.Sprintf("unable to get any namespaces: %v", err)) + return + } ingress := &networkv1.Ingress{} if err := h.Client.Get(ctx, types.NamespacedName{ Namespace: ns, @@ -81,7 +89,7 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re trueClientIP := r.Header.Get("True-Client-IP") requestUserAgent := r.Header.Get("User-Agent") - allowUnidle := h.checkAccess(ingress.ObjectMeta.Annotations, requestUserAgent, trueClientIP, xForwardedFor) + allowUnidle := h.checkAccess(namespace.ObjectMeta.Annotations, ingress.ObjectMeta.Annotations, requestUserAgent, trueClientIP, xForwardedFor) // then run checks to start to unidle the environment if allowUnidle { // if a namespace exists, it means that the custom-http-errors code is defined in the ingress object diff --git a/handlers/unidler/restrictions.go b/handlers/unidler/restrictions.go index 45e7977..41ac4d4 100644 --- a/handlers/unidler/restrictions.go +++ b/handlers/unidler/restrictions.go @@ -34,7 +34,7 @@ func checkIPList(allowList []string, xForwardedFor []string, trueClientIP string return false } -func (h *Unidler) checkAccess(annotations map[string]string, userAgent, trueClientIP string, xForwardedFor []string) bool { +func (h *Unidler) checkAccess(nsannotations map[string]string, annotations map[string]string, userAgent, trueClientIP string, xForwardedFor []string) bool { allowedIP := false allowedAgent := false blockedIP := false @@ -51,9 +51,14 @@ func (h *Unidler) checkAccess(annotations map[string]string, userAgent, trueClie hasIPAllowList = true allowedIP = checkIPList(strings.Split(alist, ","), xForwardedFor, trueClientIP) } else { - if h.AllowedIPs != nil { + if alist, ok := nsannotations["idling.amazee.io/ip-allow-list"]; ok { hasIPAllowList = true - allowedIP = checkIPList(h.AllowedIPs, xForwardedFor, trueClientIP) + allowedIP = checkIPList(strings.Split(alist, ","), xForwardedFor, trueClientIP) + } else { + if h.AllowedIPs != nil { + hasIPAllowList = true + allowedIP = checkIPList(h.AllowedIPs, xForwardedFor, trueClientIP) + } } } @@ -63,9 +68,14 @@ func (h *Unidler) checkAccess(annotations map[string]string, userAgent, trueClie hasIPBlockList = true blockedIP = checkIPList(strings.Split(blist, ","), xForwardedFor, trueClientIP) } else { - if h.BlockedIPs != nil { + if blist, ok := nsannotations["idling.amazee.io/ip-block-list"]; ok { hasIPBlockList = true - blockedIP = checkIPList(h.BlockedIPs, xForwardedFor, trueClientIP) + blockedIP = checkIPList(strings.Split(blist, ","), xForwardedFor, trueClientIP) + } else { + if h.BlockedIPs != nil { + hasIPBlockList = true + blockedIP = checkIPList(h.BlockedIPs, xForwardedFor, trueClientIP) + } } } @@ -81,9 +91,14 @@ func (h *Unidler) checkAccess(annotations map[string]string, userAgent, trueClie hasAllowedAgentList = true allowedAgent = checkAgents(strings.Split(agents, ","), userAgent) } else { - if h.AllowedUserAgents != nil { + if agents, ok := annotations["idling.amazee.io/allowed-agents"]; ok { hasAllowedAgentList = true - allowedAgent = checkAgents(h.AllowedUserAgents, userAgent) + allowedAgent = checkAgents(strings.Split(agents, ","), userAgent) + } else { + if h.AllowedUserAgents != nil { + hasAllowedAgentList = true + allowedAgent = checkAgents(h.AllowedUserAgents, userAgent) + } } } @@ -91,9 +106,14 @@ func (h *Unidler) checkAccess(annotations map[string]string, userAgent, trueClie hasBlockedAgentList = true blockedAgent = checkAgents(strings.Split(agents, ","), userAgent) } else { - if h.BlockedUserAgents != nil { + if agents, ok := annotations["idling.amazee.io/blocked-agents"]; ok { hasBlockedAgentList = true - blockedAgent = checkAgents(h.BlockedUserAgents, userAgent) + blockedAgent = checkAgents(strings.Split(agents, ","), userAgent) + } else { + if h.BlockedUserAgents != nil { + hasBlockedAgentList = true + blockedAgent = checkAgents(h.BlockedUserAgents, userAgent) + } } } diff --git a/handlers/unidler/restrictions_test.go b/handlers/unidler/restrictions_test.go index 44916a0..a8cc841 100644 --- a/handlers/unidler/restrictions_test.go +++ b/handlers/unidler/restrictions_test.go @@ -116,6 +116,7 @@ func TestUnidler_checkAccess(t *testing.T) { BlockedIPs []string } type args struct { + nsannotations map[string]string annotations map[string]string userAgent string trueClientIP string @@ -362,6 +363,25 @@ func TestUnidler_checkAccess(t *testing.T) { }, want: true, }, + { + name: "test15 - allowed ip blocked agent namespace annotation", + args: args{ + nsannotations: map[string]string{ + "idling.amazee.io/blocked-agents": "@(example).test.?$,@(internal).test.?$", + "idling.amazee.io/ip-allow-list": "1.2.3.4", + }, + userAgent: "This is not a bot, don't complaint to: complain@example.test.", + trueClientIP: "1.2.3.4", + xForwardedFor: nil, + }, + fields: fields{ + AllowedUserAgents: nil, + BlockedUserAgents: nil, + BlockedIPs: nil, + AllowedIPs: nil, + }, + want: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -371,7 +391,7 @@ func TestUnidler_checkAccess(t *testing.T) { AllowedIPs: tt.fields.AllowedIPs, BlockedIPs: tt.fields.BlockedIPs, } - if got := h.checkAccess(tt.args.annotations, tt.args.userAgent, tt.args.trueClientIP, tt.args.xForwardedFor); got != tt.want { + if got := h.checkAccess(tt.args.nsannotations, tt.args.annotations, tt.args.userAgent, tt.args.trueClientIP, tt.args.xForwardedFor); got != tt.want { t.Errorf("Unidler.checkAccess() = %v, want %v", got, tt.want) } }) diff --git a/handlers/unidler/unidler.go b/handlers/unidler/unidler.go index 1ec9589..76d95df 100644 --- a/handlers/unidler/unidler.go +++ b/handlers/unidler/unidler.go @@ -36,6 +36,7 @@ type Unidler struct { Client ctrlClient.Client Log logr.Logger RefreshInterval int + UnidlerHTTPPort int Debug bool RequestCount *prometheus.CounterVec RequestDuration *prometheus.HistogramVec @@ -106,7 +107,7 @@ func Run(h *Unidler, setupLog logr.Logger) { http.Handle("/", r) httpServer := &http.Server{ - Addr: ":5000", + Addr: fmt.Sprintf(":%d", h.UnidlerHTTPPort), Handler: r, } err := httpServer.ListenAndServe() diff --git a/main.go b/main.go index ce45513..b5c60a2 100644 --- a/main.go +++ b/main.go @@ -56,6 +56,7 @@ func main() { var enableLeaderElection bool var debug bool var refreshInterval int + var unidlerHTTPPort int var dryRun bool var selectorsFile string @@ -94,12 +95,14 @@ func main() { "Flag to determine if the idler should check the hit backend or not. If true, this overrides what is in the selectors file.") flag.BoolVar(&enableCLIIdler, "enable-cli-idler", true, "Flag to enable cli idler.") flag.BoolVar(&enableServiceIdler, "enable-service-idler", true, "Flag to enable service idler.") + flag.IntVar(&unidlerHTTPPort, "unidler-port", 5000, "Port for the unidler service to listen on.") flag.Parse() selectorsFile = variables.GetEnv("SELECTORS_YAML_FILE", selectorsFile) dryRun = variables.GetEnvBool("DRY_RUN", dryRun) + unidlerHTTPPort = variables.GetEnvInt("UNIDLER_PORT", unidlerHTTPPort) cliCron = variables.GetEnv("CLI_CRON", cliCron) serviceCron = variables.GetEnv("SERVICE_CRON", serviceCron) enableServiceIdler = variables.GetEnvBool("ENABLE_SERVICE_IDLER", enableServiceIdler) @@ -178,6 +181,7 @@ func main() { BlockedUserAgents: blockedAgents, AllowedIPs: allowedIPs, BlockedIPs: blockedIPs, + UnidlerHTTPPort: unidlerHTTPPort, } prometheusClient, err := prometheusapi.NewClient(prometheusapi.Config{