Services of type NodePort outside dev and test
What does ZopNight detect here?
ZopNight flags an EKS, GKE or AKS Service of `type: NodePort` unless labels, namespace or naming mark it as dev or test. Kubernetes opens the allocated port, drawn from 30000-32767 by default, on every node, so the Service answers on node IP addresses wherever firewall rules let traffic in, outside any Ingress routing or TLS.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1722 · RC-1822 · RC-1922 |
| Category | security |
| Severity | medium |
| Metric | spec.type |
| Threshold | NodePort, not dev or test by label, namespace or name |
| Source | ZopNight |
| Permissions used | list services |
Where it applies
The same port, open on every node
With type: NodePort, the control plane allocates a port from the range set by
--service-node-port-range, 30000-32767 by default, and each node proxies that port into the
Service. The Service documentation
adds that in kube-proxy’s iptables mode the port is available on all node IPs, while nftables mode
limits it to the node’s primary IP by default. Either way the Service is reachable directly on
node addresses, next to rather than behind whatever Ingress, TLS or authentication you run.
That leaves node firewalls as the main control. Security groups or firewall rules written for other purposes may already admit the node-port range, and a single overly broad rule exposes every NodePort Service in the cluster at once.
Listing NodePort Services and their ports
kubectl get services -A -o json | jq -r ' .items[] | select(.spec.type == "NodePort") | "\(.metadata.namespace)/\(.metadata.name) nodePorts=\([.spec.ports[].nodePort] | join(","))"'Compare those ports with the inbound rules on your node security groups or firewall.
How ZopNight picks which NodePorts to flag
The rule fires on Services whose recorded type is NodePort, then applies the same context test
used for LoadBalancer Services. An environment label (env, environment, stage or tier) set
to a production value keeps the Service flagged regardless of its name. Without one, a dev or
test label, a namespace segment such as dev, development, test, testing, qa, staging,
stage, sandbox or demo, or a name segment matching those words or canary, preview or
experiment, suppresses it, since node ports are a common shortcut for ad-hoc access in
non-production.
Services that fall outside it
LoadBalancer Services also open node ports by default, but they are reported under Service type LoadBalancer, not here. ClusterIP and ExternalName Services are never flagged. The check reads only the type, labels, namespace and name; it does not inspect your firewall rules, so it cannot tell whether a port is actually reachable from outside.
Medium severity, no saving
No dollar amount is attached. The risk is an unauthenticated, unencrypted path to a workload that bypasses the controls your Ingress layer was built to enforce.
Closing the node port
- Confirm who connects to the node port and from where.
- Change
typetoClusterIPfor traffic that stays inside the cluster. - Publish external HTTP traffic through an Ingress with TLS, as Ingress without TLS explains.
- If a node port must stay, restrict the node firewall or security group to known sources, and
consider kube-proxy’s
--nodeport-addressesflag to limit which node IPs listen.