Your cluster's control plane is on 443, and most of it has no password by default

Someone added a reverse-proxy rule to expose a dashboard, or a metrics endpoint, or a health check, and it worked. What they didn't notice is that the same proxy now forwards the raw service underneath it, and the service underneath it is the thing that runs your compute or holds your cluster's secrets. These tools were built to live on a private network where everyone who can reach the port is already trusted. So they ship with auth off, or auth optional, by default. Move them onto port 443 at your website host and that default assumption breaks in the worst possible place.

This is why infrastructure control planes show up in a web scan rather than a port scan. Apache Flink, etcd, a Docker Registry, InfluxDB, the Kubernetes API server: each one normally listens on its own internal port, talking only to the machines in its cluster. The mistake is uniform. A proxy puts the raw control plane on the public host, and now https://yoursite.com/version or https://internal.yoursite.com/v2/ answers any browser on earth, with the same no-password assumption it had on the inside.

Each of these answers a single GET with a JSON banner that names itself precisely. That's both how you find one that's exposed and how a stranger does. The identity is volunteered; you don't have to ask twice.

What each one hands a stranger

The threat is different per service, and it's worth being exact about it, because the stakes range from "they read your config" to "they run code on your cluster."

Apache Flink JobManager. Flink's REST interface exists to submit jobs, and by default it does that with no authentication and no SSL. A GET to /config returns a banner with "flink-version" and a "features" block that includes "web-submit". When web-submit is on, the same API that returns that banner accepts an uploaded job. An uploaded job is arbitrary code running on your cluster. So an exposed Flink JobManager isn't an information leak. It's unauthenticated remote code execution, sitting on the web behind a single proxy rule.

etcd. etcd is the key-value store that backs a Kubernetes cluster's entire state, including its Secrets. It's unauthenticated by default, on the assumption that only the control plane reaches it. A GET to /version returns "etcdserver" and "etcdcluster" version fields. If that banner is answering the public internet, the store underneath it is too, and that store is where your Kubernetes Secrets, your service-account tokens, and your whole cluster config live in the clear.

Docker Registry. Your private registry holds your images. A GET to /v2/ on an open registry returns the spec-constant header Docker-Distribution-Api-Version: registry/2.0. A properly secured registry answers that same path with 401 and does not fire this check, which is the point: a hit means the registry is genuinely anonymous. Anyone can then pull your private images, and image layers routinely carry baked-in credentials, the .env someone COPY'd in, the deploy key in a build step, the API token in an entrypoint. They can also push, which means they can quietly poison an image you'll later deploy.

InfluxDB. The time-series database behind a lot of metrics stacks. A GET to /health returns "name":"influxdb" and "message":"ready for queries and writes". Legacy 1.x deployments very often leave /query and /write open with no auth, so an exposed InfluxDB means a stranger can read your metrics and write fake ones into them. The banner alone tells you the database is answering the open web on 443.

Kubernetes API server. This one needs an honest framing, because overclaiming it would be the false critical that erodes trust. A GET to /version returns the apimachinery version.Info triple: "gitTreeState", "compiler":"gc", and "platform":"linux/...". On managed clusters /version is public-info; reaching it does not mean anonymous full access to the cluster. What it means is that your control plane is reachable on the public internet at all. The Kubernetes API server should sit behind a private endpoint or a VPN, not answer a browser. So the finding is "your control plane is publicly reachable, and it shouldn't be," which is a real and fixable exposure, not "anyone can run kubectl get secrets."

$ scan yoursite.com
probing yoursite.com from outside, no credentials...
/configApache Flink job-submission, auth off
/versionetcd cluster, unauth secret store
/v2/Docker Registry, anonymous pull/push
/healthInfluxDB, public on 443
4 exposures visible to anyone. None required a login.
Four banners, four control planes, all found from outside without a single credential.

The banners, up close

Take the registry, because its signal is the cleanest. The spec fixes the version header, so a real registry can't help but announce itself, and the difference between an open one and a closed one is a single status code.

request
GET /v2/ HTTP/1.1 Host: yoursite.com
response
HTTP/1.1 200 OK
Docker-Distribution-Api-Version: registry/2.0
Content-Type: application/json
{}
An unauthenticated Docker Registry. Anyone can pull your private images, and the layers carry cleartext credentials.

A secured registry answers that exact request with 401 Unauthorized and a WWW-Authenticate challenge. That case does not fire. The 200 with the registry header is the whole signal, the same way Flink's /config, etcd's /version, and InfluxDB's /health each return a 200 JSON body carrying two co-occurring literals that only the real product emits. A page that merely mentions Flink, or a docs site describing etcd, doesn't have those structural literals in a live JSON banner, so it doesn't trip the check.

Why named cases keep happening

These aren't theoretical. Open Docker Registries get found constantly by anyone scanning the /v2/ path, and the images pulled out of them have shipped real cleartext credentials more than once. Exposed etcd has leaked cluster secrets in incident after incident, precisely because it's the unauth store nobody expected to be reachable. Flink's web-submit RCE is documented and exploited in the wild. The pattern underneath all of them is the reverse-proxy rule that "just exposed the dashboard" and silently exposed the engine behind it.

Taking the control plane off 443

The fix is the same shape for every one of these, because the root cause is the same: a service built for a private network ended up on a public one.

# nginx forwarding the raw control plane to the world
location / { proxy_pass http://localhost:8081; }   # Flink JobManager
# etcd, the registry, InfluxDB, the kube-apiserver all
# default to a private network: auth off or optional
# -> anyone hitting the path reaches the engine itself
Never reverse-proxy the raw control plane to the public host. Keep it on the internal network, and turn auth on.

Start by deleting the proxy rule that forwards the raw service. Bind etcd, the registry, InfluxDB, and Flink to the internal network so only your cluster reaches them. Put the Kubernetes API server behind a private endpoint or a VPN; on a managed cluster, restrict the authorized networks to your own ranges. Then turn on the auth that each one leaves optional: SSL and authentication for Flink, client certificates for etcd, token auth for the registry, real credentials for InfluxDB. The proxy was the accident. Removing it is most of the cure.

Reading it from outside

Whether your control plane answers the public internet is something a stranger settles with one GET, and that's exactly what SurfaceCheckr does, from outside, no credentials. It issues a single request, the same one the product's own dashboard JS or a health checker would send, and reads the banner the service volunteers. It never POSTs a job to Flink, never reads a key out of etcd, never runs a query against InfluxDB. That boundary is what keeps it passive: a banner read tells you the engine is reachable, and reaching for more would cross into interrogating it. Each finding is gated on two co-occurring product-distinctive literals, so it fires on the real running service, not a page that mentions it, and the registry's secured 401 is deliberately left alone. This isn't a pentest of your cluster. It's the one question that has to land first: is the control plane reachable from a browser at all, when it was built to live where no browser can go. For the data-store cousin of this, an exposed Elasticsearch or OpenSearch cluster reads the same way, and on the panel side, an exposed Kubernetes Dashboard is the next thing to check.

Find it before someone else does.

Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.