What happens
OpenSearchConnection.getClient() prefixes an address that has no scheme with http://. If opensearch.<type>.user and opensearch.<type>.password are set, the client sends Basic authentication over that plain connection with no complaint. The credentials are registered against AuthScope.ANY, so they are offered to any host, port and realm the client ends up talking to, including nodes discovered by the sniffer, which is on by default. opensearch.disable.tls.validation turns off certificate and hostname checking in one line and logs nothing when it is on.
Where
external/opensearch/src/main/java/org/apache/stormcrawler/opensearch/OpenSearchConnection.java:102-106 and :151-153. The same three things exist in external/opensearch-java/src/main/java/org/apache/stormcrawler/opensearch/OpenSearchConnection.java:273-276 and :356-362, where the scope is new AuthScope(null, -1).
String scheme = "http";
// no scheme specified? use http
if (!host.startsWith(scheme)) {
host = "http://" + host;
}
credentialsProvider.setCredentials(
AuthScope.ANY, new UsernamePasswordCredentials(user, password));
Config keys: opensearch.<type>.addresses, opensearch.<type>.user, opensearch.<type>.password, opensearch.<type>.sniff, opensearch.disable.tls.validation.
Why it matters
The shipped external/opensearch/opensearch-conf.yaml puts opensearch.user and opensearch.password stubs directly under opensearch.addresses: "http://localhost:9200", and opensearch.indexer.addresses: "localhost" has no scheme at all. An operator who fills in the credentials and moves the cluster to another host, without also editing the scheme, sends the password in cleartext on every bulk and status request. AuthScope.ANY widens that: the sniffer learns the node list over the same unauthenticated channel, and the credentials are then offered to whatever addresses come back. Every harmful case needs the operator to have left cleartext in place, and the shipped example is loopback, so this is about failing closed rather than about a default that is wrong on its own.
Reproduction
Save as external/opensearch/src/test/java/org/apache/stormcrawler/opensearch/OpenSearchConnectionCredentialsTest.java.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.stormcrawler.opensearch;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.opensearch.client.Node;
import org.opensearch.client.RestHighLevelClient;
/**
* An address without a scheme is turned into an http:// one. Combined with opensearch.*.user and
* opensearch.*.password, that sends Basic credentials in the clear to a remote host.
*/
class OpenSearchConnectionCredentialsTest {
@Test
void credentialsAreNotSentOverPlainHttp() throws Exception {
Map<String, Object> conf = new HashMap<>();
conf.put("opensearch.indexer.addresses", "opensearch1.example.org:9200");
conf.put("opensearch.indexer.user", "crawler");
conf.put("opensearch.indexer.password", "s3cret");
try (RestHighLevelClient client = OpenSearchConnection.getClient(conf, "indexer")) {
for (Node node : client.getLowLevelClient().getNodes()) {
System.out.println("node: " + node.getHost());
assertNotEquals(
"http",
node.getHost().getSchemeName(),
"credentials configured, so the connection must not default to plain http");
}
}
}
}
Run it:
mvn -pl external/opensearch test -Dtest=OpenSearchConnectionCredentialsTest
It builds a client from a config with a scheme-less remote address plus user and password, and asserts the resulting node is not plain http. It fails on main. No server is contacted; the client is only constructed.
node: http://opensearch1.example.org:9200
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[ERROR] OpenSearchConnectionCredentialsTest.credentialsAreNotSentOverPlainHttp:44 credentials configured, so the connection must not default to plain http ==> expected: not equal but was: <http>
Suggested fix
In both OpenSearchConnection classes, refuse to start when a user and password are configured and the resulting scheme is http, with an exemption for loopback addresses so local development keeps working. Scope the credentials to the configured hosts instead of AuthScope.ANY, so a node address that arrives from sniffing does not automatically receive them. Log a warning when opensearch.disable.tls.validation is on. Change the shipped opensearch-conf.yaml and the archetype copy to show https:// next to the credential stubs, and give every address an explicit scheme. Refusing to start is a behaviour change for anyone running credentials over http today, so it needs a release note.
What happens
OpenSearchConnection.getClient()prefixes an address that has no scheme withhttp://. Ifopensearch.<type>.userandopensearch.<type>.passwordare set, the client sends Basic authentication over that plain connection with no complaint. The credentials are registered againstAuthScope.ANY, so they are offered to any host, port and realm the client ends up talking to, including nodes discovered by the sniffer, which is on by default.opensearch.disable.tls.validationturns off certificate and hostname checking in one line and logs nothing when it is on.Where
external/opensearch/src/main/java/org/apache/stormcrawler/opensearch/OpenSearchConnection.java:102-106and:151-153. The same three things exist inexternal/opensearch-java/src/main/java/org/apache/stormcrawler/opensearch/OpenSearchConnection.java:273-276and:356-362, where the scope isnew AuthScope(null, -1).Config keys:
opensearch.<type>.addresses,opensearch.<type>.user,opensearch.<type>.password,opensearch.<type>.sniff,opensearch.disable.tls.validation.Why it matters
The shipped
external/opensearch/opensearch-conf.yamlputsopensearch.userandopensearch.passwordstubs directly underopensearch.addresses: "http://localhost:9200", andopensearch.indexer.addresses: "localhost"has no scheme at all. An operator who fills in the credentials and moves the cluster to another host, without also editing the scheme, sends the password in cleartext on every bulk and status request.AuthScope.ANYwidens that: the sniffer learns the node list over the same unauthenticated channel, and the credentials are then offered to whatever addresses come back. Every harmful case needs the operator to have left cleartext in place, and the shipped example is loopback, so this is about failing closed rather than about a default that is wrong on its own.Reproduction
Save as
external/opensearch/src/test/java/org/apache/stormcrawler/opensearch/OpenSearchConnectionCredentialsTest.java.Run it:
It builds a client from a config with a scheme-less remote address plus user and password, and asserts the resulting node is not plain http. It fails on main. No server is contacted; the client is only constructed.
Suggested fix
In both
OpenSearchConnectionclasses, refuse to start when a user and password are configured and the resulting scheme is http, with an exemption for loopback addresses so local development keeps working. Scope the credentials to the configured hosts instead ofAuthScope.ANY, so a node address that arrives from sniffing does not automatically receive them. Log a warning whenopensearch.disable.tls.validationis on. Change the shippedopensearch-conf.yamland the archetype copy to showhttps://next to the credential stubs, and give every address an explicit scheme. Refusing to start is a behaviour change for anyone running credentials over http today, so it needs a release note.