88import org .apache .http .Header ;
99import org .apache .http .HttpHeaders ;
1010import org .apache .http .HttpHost ;
11+ import org .apache .http .HttpRequestInterceptor ;
1112import org .apache .http .NameValuePair ;
13+ import org .apache .http .ProtocolException ;
1214import org .apache .http .auth .AuthScope ;
1315import org .apache .http .auth .Credentials ;
1416import org .apache .http .auth .UsernamePasswordCredentials ;
1921import org .apache .http .client .methods .HttpEntityEnclosingRequestBase ;
2022import org .apache .http .client .methods .HttpHead ;
2123import org .apache .http .client .methods .HttpRequestBase ;
24+ import org .apache .http .client .methods .HttpRequestWrapper ;
2225import org .apache .http .client .protocol .HttpClientContext ;
2326import org .apache .http .client .utils .URIUtils ;
2427import org .apache .http .client .utils .URLEncodedUtils ;
3134import org .apache .http .impl .client .BasicAuthCache ;
3235import org .apache .http .impl .client .BasicCredentialsProvider ;
3336import org .apache .http .impl .client .CloseableHttpClient ;
37+ import org .apache .http .impl .client .DefaultRedirectStrategy ;
3438import org .apache .http .impl .client .HttpClientBuilder ;
3539import org .apache .http .message .BasicNameValuePair ;
40+ import org .apache .http .protocol .HttpContext ;
3641import org .apache .logging .log4j .LogManager ;
3742import org .apache .logging .log4j .Logger ;
43+ import org .apache .lucene .util .automaton .Automaton ;
44+ import org .apache .lucene .util .automaton .CharacterRunAutomaton ;
45+ import org .apache .lucene .util .automaton .MinimizationOperations ;
46+ import org .apache .lucene .util .automaton .Operations ;
47+ import org .elasticsearch .ElasticsearchException ;
48+ import org .elasticsearch .cluster .service .ClusterService ;
3849import org .elasticsearch .common .Strings ;
50+ import org .elasticsearch .common .regex .Regex ;
3951import org .elasticsearch .common .settings .Settings ;
4052import org .elasticsearch .common .unit .ByteSizeValue ;
4153import org .elasticsearch .common .unit .TimeValue ;
5971import java .util .HashMap ;
6072import java .util .List ;
6173import java .util .Map ;
74+ import java .util .concurrent .atomic .AtomicReference ;
6275
6376public class HttpClient implements Closeable {
6477
@@ -69,20 +82,29 @@ public class HttpClient implements Closeable {
6982 private static final int MAX_CONNECTIONS = 500 ;
7083 private static final Logger logger = LogManager .getLogger (HttpClient .class );
7184
85+ private final AtomicReference <CharacterRunAutomaton > whitelistAutomaton = new AtomicReference <>();
7286 private final CloseableHttpClient client ;
7387 private final HttpProxy settingsProxy ;
7488 private final TimeValue defaultConnectionTimeout ;
7589 private final TimeValue defaultReadTimeout ;
7690 private final ByteSizeValue maxResponseSize ;
7791 private final CryptoService cryptoService ;
92+ private final SSLService sslService ;
7893
79- public HttpClient (Settings settings , SSLService sslService , CryptoService cryptoService ) {
94+ public HttpClient (Settings settings , SSLService sslService , CryptoService cryptoService , ClusterService clusterService ) {
8095 this .defaultConnectionTimeout = HttpSettings .CONNECTION_TIMEOUT .get (settings );
8196 this .defaultReadTimeout = HttpSettings .READ_TIMEOUT .get (settings );
8297 this .maxResponseSize = HttpSettings .MAX_HTTP_RESPONSE_SIZE .get (settings );
8398 this .settingsProxy = getProxyFromSettings (settings );
8499 this .cryptoService = cryptoService ;
100+ this .sslService = sslService ;
85101
102+ setWhitelistAutomaton (HttpSettings .HOSTS_WHITELIST .get (settings ));
103+ clusterService .getClusterSettings ().addSettingsUpdateConsumer (HttpSettings .HOSTS_WHITELIST , this ::setWhitelistAutomaton );
104+ this .client = createHttpClient ();
105+ }
106+
107+ private CloseableHttpClient createHttpClient () {
86108 HttpClientBuilder clientBuilder = HttpClientBuilder .create ();
87109
88110 // ssl setup
@@ -95,8 +117,48 @@ public HttpClient(Settings settings, SSLService sslService, CryptoService crypto
95117 clientBuilder .evictExpiredConnections ();
96118 clientBuilder .setMaxConnPerRoute (MAX_CONNECTIONS );
97119 clientBuilder .setMaxConnTotal (MAX_CONNECTIONS );
120+ clientBuilder .setRedirectStrategy (new DefaultRedirectStrategy () {
121+ @ Override
122+ public boolean isRedirected (org .apache .http .HttpRequest request , org .apache .http .HttpResponse response ,
123+ HttpContext context ) throws ProtocolException {
124+ boolean isRedirected = super .isRedirected (request , response , context );
125+ if (isRedirected ) {
126+ String host = response .getHeaders ("Location" )[0 ].getValue ();
127+ if (isWhitelisted (host ) == false ) {
128+ throw new ElasticsearchException ("host [" + host + "] is not whitelisted in setting [" +
129+ HttpSettings .HOSTS_WHITELIST .getKey () + "], will not redirect" );
130+ }
131+ }
132+
133+ return isRedirected ;
134+ }
135+ });
136+
137+ clientBuilder .addInterceptorFirst ((HttpRequestInterceptor ) (request , context ) -> {
138+ if (request instanceof HttpRequestWrapper == false ) {
139+ throw new ElasticsearchException ("unable to check request [{}/{}] for white listing" , request ,
140+ request .getClass ().getName ());
141+ }
142+
143+ HttpRequestWrapper wrapper = ((HttpRequestWrapper ) request );
144+ final String host ;
145+ if (wrapper .getTarget () != null ) {
146+ host = wrapper .getTarget ().toURI ();
147+ } else {
148+ host = wrapper .getOriginal ().getRequestLine ().getUri ();
149+ }
98150
99- client = clientBuilder .build ();
151+ if (isWhitelisted (host ) == false ) {
152+ throw new ElasticsearchException ("host [" + host + "] is not whitelisted in setting [" +
153+ HttpSettings .HOSTS_WHITELIST .getKey () + "], will not connect" );
154+ }
155+ });
156+
157+ return clientBuilder .build ();
158+ }
159+
160+ private void setWhitelistAutomaton (List <String > whiteListedHosts ) {
161+ whitelistAutomaton .set (createAutomaton (whiteListedHosts ));
100162 }
101163
102164 public HttpResponse execute (HttpRequest request ) throws IOException {
@@ -285,6 +347,24 @@ final class HttpMethodWithEntity extends HttpEntityEnclosingRequestBase {
285347 public String getMethod () {
286348 return methodName ;
287349 }
350+
288351 }
289352
353+ private boolean isWhitelisted (String host ) {
354+ return whitelistAutomaton .get ().run (host );
355+ }
356+
357+ private static final CharacterRunAutomaton MATCH_ALL_AUTOMATON = new CharacterRunAutomaton (Regex .simpleMatchToAutomaton ("*" ));
358+ // visible for testing
359+ static CharacterRunAutomaton createAutomaton (List <String > whiteListedHosts ) {
360+ if (whiteListedHosts .isEmpty ()) {
361+ // the default is to accept everything, this should change in the next major version, being 8.0
362+ // we could emit depreciation warning here, if the whitelist is empty
363+ return MATCH_ALL_AUTOMATON ;
364+ }
365+
366+ Automaton whiteListAutomaton = Regex .simpleMatchToAutomaton (whiteListedHosts .toArray (Strings .EMPTY_ARRAY ));
367+ whiteListAutomaton = MinimizationOperations .minimize (whiteListAutomaton , Operations .DEFAULT_MAX_DETERMINIZED_STATES );
368+ return new CharacterRunAutomaton (whiteListAutomaton );
369+ }
290370}
0 commit comments