Services of type LoadBalancer outside dev and test
What does ZopNight detect here?
ZopNight flags an EKS, GKE or AKS Service of `type: LoadBalancer` unless its labels, namespace or name mark it as dev or test. Each such Service asks the cloud provider for a load balancer of its own, which AWS, for example, bills for every hour or partial hour it runs, and which gives the Service an endpoint outside the cluster.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1721 · RC-1821 · RC-1921 |
| Category | security |
| Severity | low |
| Metric | spec.type |
| Threshold | LoadBalancer, not dev or test by label, namespace or name |
| Source | ZopNight |
| Permissions used | list services |
Where it applies
A load balancer per Service adds exposure and cost
On clouds that support external load balancers, setting a Service’s type to LoadBalancer
provisions one for that Service, as the
Service documentation
describes, with its address published in .status.loadBalancer. Every such Service is another
endpoint reachable from beyond the cluster and another resource on the bill. On AWS, the
Elastic Load Balancing pricing page charges
Application and Network Load Balancers for each hour or partial hour they run, plus capacity
units.
LoadBalancer Services also allocate node ports by default, because allocateLoadBalancerNodePorts
defaults to true, so the Service is reachable on a port of every node as well. Many clusters only
need one external entry point, an ingress controller, with everything else behind ClusterIP.
Listing LoadBalancer Services
kubectl get services -A -o json | jq -r ' .items[] | select(.spec.type == "LoadBalancer") | "\(.metadata.namespace)/\(.metadata.name) \(.status.loadBalancer.ingress // [] | map(.ip // .hostname) | join(","))"'Check the annotations too; an internal-load-balancer annotation means the address is private.
Type LoadBalancer, minus dev and test
ZopNight reads the type recorded for each Service and fires on LoadBalancer. It then checks the
Service’s labels: a key of env, environment, stage or tier holding prod, production,
prd or live always keeps it in scope. Otherwise the Service is dropped when that label says
dev, test, qa, staging, sandbox, demo, among other non-production values. Next, it is
dropped when a hyphen-, dot- or underscore-separated segment of its namespace is dev,
development, test, testing, qa, staging, stage, sandbox or demo, and last when such
a segment of its name is one of those words or canary, preview or experiment.
Neighbouring findings
The check does not read internal-load-balancer annotations, so a private load balancer is flagged the same way. A LoadBalancer Service still waiting for its address is reported by LoadBalancer service stuck pending, one with no backing pods by Service with no endpoints, and node-port exposure by Service type NodePort.
Low severity, and no computed saving
ZopNight attaches no savings figure to this advisory. The value of acting is a smaller external attack surface and, when you consolidate, fewer load balancers billed each hour.
Consolidating or restricting the endpoint
- Confirm whether clients outside the cluster need this Service.
- If not, change
typetoClusterIP. - If HTTP traffic needs to be public, route it through an Ingress so many Services share one load balancer.
- If only your private network needs it, add the provider’s internal annotation:
service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"on AWS,networking.gke.io/load-balancer-type: "Internal"on GKE, orservice.beta.kubernetes.io/azure-load-balancer-internal: "true"on AKS.