admin: bind worker gRPC listener to -ip instead of wildcard (#11300)

* admin: bind worker gRPC listener to -ip instead of wildcard

The worker/plugin gRPC control plane called net.Listen("tcp", ":port")
directly, so it wildcard-bound every interface and ignored the -ip setting.
A cluster bound to loopback still exposed the unauthenticated
WorkerService/PluginControlService streams on 0.0.0.0. Bind through
util.JoinHostPort(bindIp, port) so the listener honors -ip like the
master, filer, and volume gRPC listeners.

* admin: warn when worker gRPC is exposed off loopback without mTLS

The worker gRPC stream has no password auth, so grpc.admin mTLS is the
only effective control once the listener leaves loopback. An operator who
sets -adminPassword and binds -ip=0.0.0.0 authenticates the HTTP API but
still exposes the unauthenticated worker control plane. Log a startup
warning naming the port and the mTLS knobs so the exposure is not silent.

* admin: address review on worker gRPC bind fix

- mini: reserve the admin gRPC port with util.JoinHostPort so an IPv6
  bindIp (e.g. ::1) does not form an invalid unbracketed address and
  lose the reservation.
- worker gRPC: track whether grpc.admin mTLS credentials actually loaded
  rather than only whether they were configured, and gate the
  non-loopback exposure warning on that. A cert/key that fails to load
  now still warns instead of silently suppressing.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-09-13 21:48:14 -07:00
1 parent f4bad510c9
commit cf38c01978
4 files changed
+41 -13

No files matched your search

+18 -1
View File
@@ -461,10 +461,11 @@ func startAdminServer(ctx context.Context, options AdminOptions, enableUI bool,
}
// Start worker gRPC server for worker connections
err = adminServer.StartWorkerGrpcServer(*options.grpcPort, options.workerGrpcListener)
err = adminServer.StartWorkerGrpcServer(*options.ip, *options.grpcPort, options.workerGrpcListener)
if err != nil {
return fmt.Errorf("failed to start worker gRPC server: %w", err)
}
warnInsecureWorkerGrpcBind(*options.ip, *options.grpcPort, adminServer.WorkerGrpcMTLSEnabled())
// Set up cleanup for gRPC server
defer func() {
@@ -758,3 +759,19 @@ func isLoopbackIp(ip string) bool {
}
return parsed.IsLoopback()
}
// warnInsecureWorkerGrpcBind warns when the worker gRPC control plane is
// reachable off loopback without grpc.admin mTLS, its only auth once exposed.
// mtlsEnabled reflects whether the worker gRPC actually loaded mTLS, so a
// misconfigured cert/key that fails to load still triggers the warning.
func warnInsecureWorkerGrpcBind(ip string, grpcPort int, mtlsEnabled bool) {
if isLoopbackIp(ip) {
return
}
if mtlsEnabled {
return
}
glog.Warningf("Worker gRPC control plane is bound to %s (non-loopback) without grpc.admin mTLS.", ip)
glog.Warningf("Anyone who can reach port %d can register a maintenance worker unauthenticated.", grpcPort)
glog.Warningf("Enable [grpc.admin] cert/key and [grpc.ca] in security.toml, or bind to loopback.")
}