Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bind the readiness service to the wildcard address #91329

Merged
merged 12 commits into from
Nov 16, 2022
3 changes: 2 additions & 1 deletion distribution/src/config/elasticsearch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
#
# --------------------------------- Readiness ----------------------------------
#
# Enable an unauthenticated TCP readiness endpoint on localhost
# Enable an unauthenticated TCP readiness endpoint. The readiness service binds to all
# host addresses.
#
#readiness.port: 9399
#
Expand Down
6 changes: 6 additions & 0 deletions docs/changelog/91329.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 91329
summary: Allow configuring readiness host
area: Infra/Core
type: enhancement
issues:
- 90997
6 changes: 2 additions & 4 deletions docs/reference/setup/advanced-configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,8 @@ If configured, a node can open a TCP port when the node is in a ready state. A n
ready when it has successfully joined a cluster. In a single node configuration, the node is
said to be ready, when it's able to accept requests.

To enable the readiness TCP port, use the `readiness.port` setting. The port is
always bound to the loopback address, which defaults to the IPv4 loopback address `127.0.0.1`.
To bind the readiness port to the IPv6 loopback address `::1`,
add `-Djava.net.preferIPv6Addresses=true` to the <<set-jvm-options,JVM options>>.
To enable the readiness TCP port, use the `readiness.port` setting. The readiness service will bind to
all host addresses.

If the node leaves the cluster, or the <<put-shutdown,Shutdown API>> is used to mark the node
for shutdown, the readiness port is immediately closed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.network.NetworkAddress;
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.transport.BoundTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
Expand All @@ -31,11 +33,14 @@
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_BIND_HOST;

public class ReadinessService extends AbstractLifecycleComponent implements ClusterStateListener {
private static final Logger logger = LogManager.getLogger(ReadinessService.class);

Expand Down Expand Up @@ -102,18 +107,32 @@ InetSocketAddress socketAddress(InetAddress host, int portNumber) {

// package private for testing
ServerSocketChannel setupSocket() {
InetAddress localhost = InetAddress.getLoopbackAddress();
int portNumber = PORT.get(environment.settings());
var settings = environment.settings();
int portNumber = PORT.get(settings);
assert portNumber >= 0;

List<String> httpBindHost = SETTING_HTTP_BIND_HOST.get(settings);
var bindHosts = (httpBindHost.isEmpty() ? NetworkService.GLOBAL_NETWORK_BIND_HOST_SETTING.get(settings) : httpBindHost).toArray(
Strings.EMPTY_ARRAY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be two levels of configuration before the fallback to the loopback. Do we need the configuration checks? And should the fallback be to the wildcard address which implies all (rather than to the loopback) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled with this a lot, what's the right address to pick, 0, publish, bound? I borrowed this code from how we setup the bound addresses in AbstractHttpServerTransport, I don't mind binding to 0 (all) as fallback, but if a user doesn't configure any of those above for Elasticsearch, they will get ES bound to localhost for HTTP, unless they configure SETTING_HTTP_PUBLISH_HOST. I don't know if binding to localhost and publishing another host address is a valid configuration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed it with @rjernst just now, we decided that perhaps the easiest is to bind for 0 and if there are any concerns firewall rules should be used to limit access to the port.

);

var socketAddress = AccessController.doPrivileged((PrivilegedAction<InetSocketAddress>) () -> {
try {
var bindHost = (bindHosts.length == 0) ? InetAddress.getLoopbackAddress() : InetAddress.getByName(bindHosts[0]);
return socketAddress(bindHost, portNumber);
} catch (IOException e) {
throw new IllegalArgumentException("Failed to resolve readiness host address", e);
}
});

try {
serverChannel = ServerSocketChannel.open();

AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
try {
serverChannel.bind(socketAddress(localhost, portNumber));
serverChannel.bind(socketAddress);
} catch (IOException e) {
throw new BindTransportException("Failed to bind to " + NetworkAddress.format(localhost, portNumber), e);
throw new BindTransportException("Failed to bind to " + NetworkAddress.format(socketAddress), e);
}
return null;
});
Expand All @@ -129,7 +148,7 @@ ServerSocketChannel setupSocket() {
}
}
} catch (Exception e) {
throw new BindTransportException("Failed to open socket channel " + NetworkAddress.format(localhost, portNumber), e);
throw new BindTransportException("Failed to open socket channel " + NetworkAddress.format(socketAddress), e);
}

return serverChannel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ grant codeBase "${codebase.elasticsearch}" {
// needed for loading plugins which may expect the context class loader to be set
permission java.lang.RuntimePermission "setContextClassLoader";
// needed for the readiness service
permission java.net.SocketPermission "127.0.0.1", "listen, accept";
// required if started with -Djava.net.preferIPv6Addresses=true
permission java.net.SocketPermission "0:0:0:0:0:0:0:1", "listen, accept";
permission java.net.SocketPermission "*", "listen, accept";

// for module layer
permission java.lang.RuntimePermission "createClassLoader";
Expand Down