mirror of https://github.com/apache/druid.git
Druid Extension to enable Authentication using Kerberos. (#3853)
* Add extension for supporting kerberos security - This PR adds an extension for supporting druid authentication via Kerberos. - Working on the docs. * Add docs * review comments * more review comments * Block all paths by default * more review comments - use proper Oid * Allow extensions to override httpclient for integration tests * Add kerberos lock to prevent multithreaded issues. * review comment - remove enabled flag and fix router injection * Add Cookie Handling and more detailed docs * review comment - rename DruidKerberosConfig -> AuthKerberosConfig * review comments * fix travis failure on jdk7
This commit is contained in:
parent
182261f713
commit
a457cded28
|
@ -106,6 +106,8 @@
|
|||
<argument>io.druid.extensions:postgresql-metadata-storage</argument>
|
||||
<argument>-c</argument>
|
||||
<argument>io.druid.extensions.contrib:scan-query</argument>
|
||||
<argument>-c</argument>
|
||||
<argument>io.druid.extensions:druid-kerberos</argument>
|
||||
<argument>${druid.distribution.pulldeps.opts}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
layout: doc_page
|
||||
---
|
||||
|
||||
# Druid-Kerberos
|
||||
|
||||
Druid Extension to enable Authentication for Druid Nodes using Kerberos.
|
||||
This extension adds AuthenticationFilter which is used to protect HTTP Endpoints using the simple and protected GSSAPI negotiation mechanism [SPNEGO](https://en.wikipedia.org/wiki/SPNEGO).
|
||||
Make sure to [include](../../operations/including-extensions.html) `druid-kerberos` as an extension.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|Property|Possible Values|Description|Default|required|
|
||||
|--------|---------------|-----------|-------|--------|
|
||||
|`druid.hadoop.security.kerberos.principal`|`druid@EXAMPLE.COM`| Principal user name, used for internal node communication|empty|Yes|
|
||||
|`druid.hadoop.security.kerberos.keytab`|`/etc/security/keytabs/druid.keytab`|Path to keytab file used for internal node communication|empty|Yes|
|
||||
|`druid.hadoop.security.spnego.principal`|`HTTP/_HOST@EXAMPLE.COM`| SPNego service principal used by druid nodes|empty|Yes|
|
||||
|`druid.hadoop.security.spnego.keytab`|`/etc/security/keytabs/spnego.service.keytab`|SPNego service keytab used by druid nodes|empty|Yes|
|
||||
|`druid.hadoop.security.spnego.authToLocal`|`RULE:[1:$1@$0](druid@EXAMPLE.COM)s/.*/druid DEFAULT`|It allows you to set a general rule for mapping principal names to local user names. It will be used if there is not an explicit mapping for the principal name that is being translated.|DEFAULT|No|
|
||||
|`druid.hadoop.security.spnego.excludedPaths`|`['/status','/health']`| Array of HTTP paths which which does NOT need to be authenticated.|None|No|
|
||||
|`druid.hadoop.security.spnego.cookieSignatureSecret`|`secretString`| Secret used to sign authentication cookies. It is advisable to explicitly set it, if you have multiple druid ndoes running on same machine with different ports as the Cookie Specification does not guarantee isolation by port.|<Random value>|No|
|
||||
|
||||
As a note, it is required that the SPNego principal in use by the druid nodes must start with HTTP (This specified by [RFC-4559](https://tools.ietf.org/html/rfc4559)) and must be of the form "HTTP/_HOST@REALM".
|
||||
The special string _HOST will be replaced automatically with the value of config `druid.host`
|
||||
|
||||
### Auth to Local Syntax
|
||||
|
||||
|
||||
`druid.hadoop.security.spnego.authToLocal` allows you to set a general rules for mapping principal names to local user names.
|
||||
The syntax for mapping rules is `RULE:\[n:string](regexp)s/pattern/replacement/g`. The integer n indicates how many components the target principal should have. If this matches, then a string will be formed from string, substituting the realm of the principal for $0 and the n‘th component of the principal for $n. e.g. if the principal was druid/admin then `\[2:$2$1suffix]` would result in the string `admindruidsuffix`.
|
||||
If this string matches regexp, then the s//\[g] substitution command will be run over the string. The optional g will cause the substitution to be global over the string, instead of replacing only the first match in the string.
|
||||
If required, multiple rules can be be joined by newline character and specified as a String.
|
||||
|
||||
## Accessing Druid HTTP end points when kerberos security is enabled
|
||||
1. To access druid HTTP endpoints via curl user will need to first login using `kinit` command as follows -
|
||||
|
||||
```
|
||||
kinit -k -t <path_to_keytab_file> user@REALM.COM
|
||||
```
|
||||
|
||||
2. Once the login is successful verify that login is successful using `klist` command
|
||||
3. Now you can access druid HTTP endpoints using curl command as follows -
|
||||
|
||||
```
|
||||
curl --negotiate -u:anyUser -b ~/cookies.txt -c ~/cookies.txt -X POST -H'Content-Type: application/json' <HTTP_END_POINT>
|
||||
```
|
||||
|
||||
e.g to send a query from file `query.json` to druid broker use this command -
|
||||
|
||||
```
|
||||
curl --negotiate -u:anyUser -b ~/cookies.txt -c ~/cookies.txt -X POST -H'Content-Type: application/json' http://broker-host:port/druid/v2/?pretty -d @query.json
|
||||
```
|
||||
Note: Above command will authenticate the user first time using SPNego negotiate mechanism and store the authentication cookie in file. For subsequent requests the cookie will be used for authentication.
|
||||
|
||||
## Accessing coordinator or overlord console from web browser
|
||||
To access Coordinator/Overlord console from browser you will need to configure your browser for SPNego authentication as follows -
|
||||
|
||||
1. Safari - No configurations required.
|
||||
2. Firefox - Open firefox and follow these steps -
|
||||
1. Go to `about:config` and search for `network.negotiate-auth.trusted-uris`.
|
||||
2. Double-click and add the following values: `"http://druid-coordinator-hostname:ui-port"` and `"http://druid-overlord-hostname:port"`
|
||||
3. Google Chrome - From the command line run following commands -
|
||||
1. `google-chrome --auth-server-whitelist="druid-coordinator-hostname" --auth-negotiate-delegate-whitelist="druid-coordinator-hostname"`
|
||||
2. `google-chrome --auth-server-whitelist="druid-overlord-hostname" --auth-negotiate-delegate-whitelist="druid-overlord-hostname"`
|
||||
4. Internet Explorer -
|
||||
1. Configure trusted websites to include `"druid-coordinator-hostname"` and `"druid-overlord-hostname"`
|
||||
2. Allow negotiation for the UI website.
|
||||
|
||||
## Sending Queries programmatically
|
||||
Many HTTP client libraries, such as Apache Commons [HttpComponents](https://hc.apache.org/), already have support for performing SPNEGO authentication. You can use any of the available HTTP client library to communicate with druid cluster.
|
|
@ -29,6 +29,7 @@ Core extensions are maintained by Druid committers.
|
|||
|druid-kafka-eight|Kafka ingest firehose (high level consumer) for realtime nodes.|[link](../development/extensions-core/kafka-eight-firehose.html)|
|
||||
|druid-kafka-extraction-namespace|Kafka-based namespaced lookup. Requires namespace lookup extension.|[link](../development/extensions-core/kafka-extraction-namespace.html)|
|
||||
|druid-kafka-indexing-service|Supervised exactly-once Kafka ingestion for the indexing service.|[link](../development/extensions-core/kafka-ingestion.html)|
|
||||
|druid-kerberos|Kerberos authentication for druid nodes.|[link](../development/extensions-core/druid-kerberos.html)|
|
||||
|druid-lookups-cached-global|A module for [lookups](../querying/lookups.html) providing a jvm-global eager caching for lookups. It provides JDBC and URI implementations for fetching lookup data.|[link](../development/extensions-core/lookups-cached-global.html)|
|
||||
|druid-lookups-cached-single| Per lookup caching module to support the use cases where a lookup need to be isolated from the global pool of lookups |[link](../development/extensions-core/druid-lookups.html)|
|
||||
|druid-s3-extensions|Interfacing with data in AWS S3, and using S3 as deep storage.|[link](../development/extensions-core/s3.html)|
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
~ or more contributor license agreements. See the NOTICE file
|
||||
~ distributed with this work for additional information
|
||||
~ regarding copyright ownership. Metamarkets 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>io.druid.extensions</groupId>
|
||||
<artifactId>druid-kerberos</artifactId>
|
||||
<name>druid-kerberos</name>
|
||||
<description>druid-kerberos</description>
|
||||
|
||||
<parent>
|
||||
<groupId>io.druid</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>0.9.3-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.druid</groupId>
|
||||
<artifactId>druid-processing</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.druid</groupId>
|
||||
<artifactId>druid-server</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-proxy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
<version>${hadoop.compile.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpcore</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.zookeeper</groupId>
|
||||
<artifactId>zookeeper</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>jsr311-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Tests -->
|
||||
<dependency>
|
||||
<groupId>io.druid</groupId>
|
||||
<artifactId>druid-processing</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class AuthenticationKerberosConfig
|
||||
{
|
||||
@JsonProperty
|
||||
private final String principal;
|
||||
@JsonProperty
|
||||
private final String keytab;
|
||||
|
||||
@JsonCreator
|
||||
public AuthenticationKerberosConfig(@JsonProperty("principal") String principal, @JsonProperty("keytab") String keytab)
|
||||
{
|
||||
this.principal = principal;
|
||||
this.keytab = keytab;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getPrincipal()
|
||||
{
|
||||
return principal;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getKeytab()
|
||||
{
|
||||
return keytab;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof AuthenticationKerberosConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AuthenticationKerberosConfig that = (AuthenticationKerberosConfig) o;
|
||||
|
||||
if (getPrincipal() != null ? !getPrincipal().equals(that.getPrincipal()) : that.getPrincipal() != null) {
|
||||
return false;
|
||||
}
|
||||
return getKeytab() != null ? getKeytab().equals(that.getKeytab()) : that.getKeytab() == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = getPrincipal() != null ? getPrincipal().hashCode() : 0;
|
||||
result = 31 * result + (getKeytab() != null ? getKeytab().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import com.metamx.http.client.HttpClient;
|
||||
import io.druid.guice.JsonConfigProvider;
|
||||
import io.druid.guice.LazySingleton;
|
||||
import io.druid.guice.annotations.Client;
|
||||
import io.druid.guice.annotations.Global;
|
||||
import io.druid.guice.http.HttpClientModule;
|
||||
import io.druid.guice.http.JettyHttpClientModule;
|
||||
import io.druid.initialization.DruidModule;
|
||||
import io.druid.server.initialization.jetty.ServletFilterHolder;
|
||||
import io.druid.server.router.Router;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class DruidKerberosModule implements DruidModule
|
||||
{
|
||||
|
||||
@Override
|
||||
public List<? extends Module> getJacksonModules()
|
||||
{
|
||||
return ImmutableList.of(
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder)
|
||||
{
|
||||
JsonConfigProvider.bind(binder, "druid.hadoop.security.kerberos", AuthenticationKerberosConfig.class);
|
||||
JsonConfigProvider.bind(binder, "druid.hadoop.security.spnego", SpnegoFilterConfig.class);
|
||||
|
||||
Multibinder.newSetBinder(binder, ServletFilterHolder.class)
|
||||
.addBinding()
|
||||
.to(SpnegoFilterHolder.class);
|
||||
|
||||
binder.bind(HttpClient.class)
|
||||
.annotatedWith(Global.class)
|
||||
.toProvider(new KerberosHttpClientProvider(new HttpClientModule.HttpClientProvider(Global.class)))
|
||||
.in(LazySingleton.class);
|
||||
|
||||
binder.bind(HttpClient.class)
|
||||
.annotatedWith(Client.class)
|
||||
.toProvider(new KerberosHttpClientProvider(new HttpClientModule.HttpClientProvider(Client.class)))
|
||||
.in(LazySingleton.class);
|
||||
|
||||
binder.bind(org.eclipse.jetty.client.HttpClient.class)
|
||||
.annotatedWith(Router.class)
|
||||
.toProvider(new KerberosJettyHttpClientProvider(new JettyHttpClientModule.HttpClientProvider(Router.class)))
|
||||
.in(LazySingleton.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import io.druid.java.util.common.ISE;
|
||||
import io.druid.java.util.common.logger.Logger;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
|
||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
||||
import org.apache.hadoop.security.authentication.util.KerberosUtil;
|
||||
import org.ietf.jgss.GSSContext;
|
||||
import org.ietf.jgss.GSSException;
|
||||
import org.ietf.jgss.GSSManager;
|
||||
import org.ietf.jgss.GSSName;
|
||||
import org.ietf.jgss.Oid;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieStore;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class DruidKerberosUtil
|
||||
{
|
||||
private static final Logger log = new Logger(DruidKerberosUtil.class);
|
||||
|
||||
private static final Base64 base64codec = new Base64(0);
|
||||
|
||||
// A fair reentrant lock
|
||||
private static ReentrantLock kerberosLock = new ReentrantLock(true);
|
||||
|
||||
/**
|
||||
* This method always needs to be called within a doAs block so that the client's TGT credentials
|
||||
* can be read from the Subject.
|
||||
*
|
||||
* @return Kerberos Challenge String
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
public static String kerberosChallenge(String server) throws AuthenticationException
|
||||
{
|
||||
kerberosLock.lock();
|
||||
try {
|
||||
// This Oid for Kerberos GSS-API mechanism.
|
||||
Oid mechOid = KerberosUtil.getOidInstance("GSS_KRB5_MECH_OID");
|
||||
GSSManager manager = GSSManager.getInstance();
|
||||
// GSS name for server
|
||||
GSSName serverName = manager.createName("HTTP@" + server, GSSName.NT_HOSTBASED_SERVICE);
|
||||
// Create a GSSContext for authentication with the service.
|
||||
// We're passing client credentials as null since we want them to be read from the Subject.
|
||||
GSSContext gssContext =
|
||||
manager.createContext(serverName.canonicalize(mechOid), mechOid, null, GSSContext.DEFAULT_LIFETIME);
|
||||
gssContext.requestMutualAuth(true);
|
||||
gssContext.requestCredDeleg(true);
|
||||
// Establish context
|
||||
byte[] inToken = new byte[0];
|
||||
byte[] outToken = gssContext.initSecContext(inToken, 0, inToken.length);
|
||||
gssContext.dispose();
|
||||
// Base64 encoded and stringified token for server
|
||||
return new String(base64codec.encode(outToken));
|
||||
}
|
||||
catch (GSSException | IllegalAccessException | NoSuchFieldException | ClassNotFoundException e) {
|
||||
throw new AuthenticationException(e);
|
||||
}
|
||||
finally {
|
||||
kerberosLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void authenticateIfRequired(AuthenticationKerberosConfig config)
|
||||
throws IOException
|
||||
{
|
||||
String principal = config.getPrincipal();
|
||||
String keytab = config.getKeytab();
|
||||
if (!Strings.isNullOrEmpty(principal) && !Strings.isNullOrEmpty(keytab)) {
|
||||
Configuration conf = new Configuration();
|
||||
conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
|
||||
UserGroupInformation.setConfiguration(conf);
|
||||
try {
|
||||
if (UserGroupInformation.getCurrentUser().hasKerberosCredentials() == false
|
||||
|| !UserGroupInformation.getCurrentUser().getUserName().equals(principal)) {
|
||||
log.info("trying to authenticate user [%s] with keytab [%s]", principal, keytab);
|
||||
UserGroupInformation.loginUserFromKeytab(principal, keytab);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ISE(e, "Failed to authenticate user principal [%s] with keytab [%s]", principal, keytab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean needToSendCredentials(CookieStore cookieStore, URI uri){
|
||||
return getAuthCookie(cookieStore, uri) == null;
|
||||
}
|
||||
|
||||
public static HttpCookie getAuthCookie(CookieStore cookieStore, URI uri)
|
||||
{
|
||||
if (cookieStore == null) {
|
||||
return null;
|
||||
}
|
||||
boolean isSSL = uri.getScheme().equals("https");
|
||||
List<HttpCookie> cookies = cookieStore.getCookies();
|
||||
|
||||
for (HttpCookie c : cookies) {
|
||||
// If this is a secured cookie and the current connection is non-secured,
|
||||
// then, skip this cookie. We need to skip this cookie because, the cookie
|
||||
// replay will not be transmitted to the server.
|
||||
if (c.getSecure() && !isSSL) {
|
||||
continue;
|
||||
}
|
||||
if (c.getName().equals(AuthenticatedURL.AUTH_COOKIE)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void removeAuthCookie(CookieStore cookieStore, URI uri)
|
||||
{
|
||||
HttpCookie authCookie = getAuthCookie(cookieStore, uri);
|
||||
if (authCookie != null) {
|
||||
cookieStore.remove(uri, authCookie);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.metamx.http.client.AbstractHttpClient;
|
||||
import com.metamx.http.client.HttpClient;
|
||||
import com.metamx.http.client.Request;
|
||||
import com.metamx.http.client.response.HttpResponseHandler;
|
||||
import io.druid.concurrent.Execs;
|
||||
import io.druid.java.util.common.logger.Logger;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class KerberosHttpClient extends AbstractHttpClient
|
||||
{
|
||||
private static final Logger log = new Logger(KerberosHttpClient.class);
|
||||
|
||||
private final HttpClient delegate;
|
||||
private final AuthenticationKerberosConfig config;
|
||||
private final CookieManager cookieManager;
|
||||
private final Executor exec = Execs.singleThreaded("test-%s");
|
||||
|
||||
public KerberosHttpClient(HttpClient delegate, AuthenticationKerberosConfig config)
|
||||
{
|
||||
this.delegate = delegate;
|
||||
this.config = config;
|
||||
this.cookieManager = new CookieManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <Intermediate, Final> ListenableFuture<Final> go(
|
||||
Request request, HttpResponseHandler<Intermediate, Final> httpResponseHandler, Duration duration
|
||||
)
|
||||
{
|
||||
final SettableFuture<Final> retVal = SettableFuture.create();
|
||||
inner_go(request, httpResponseHandler, duration, retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
private <Intermediate, Final> void inner_go(
|
||||
final Request request,
|
||||
final HttpResponseHandler<Intermediate, Final> httpResponseHandler,
|
||||
final Duration duration,
|
||||
final SettableFuture<Final> future
|
||||
)
|
||||
{
|
||||
try {
|
||||
final String host = request.getUrl().getHost();
|
||||
final URI uri = request.getUrl().toURI();
|
||||
|
||||
|
||||
Map<String, List<String>> cookieMap = cookieManager.get(uri, Collections.<String, List<String>>emptyMap());
|
||||
for (Map.Entry<String, List<String>> entry : cookieMap.entrySet()) {
|
||||
request.addHeaderValues(entry.getKey(), entry.getValue());
|
||||
}
|
||||
final boolean should_retry_on_unauthorized_response;
|
||||
|
||||
if (DruidKerberosUtil.needToSendCredentials(cookieManager.getCookieStore(), uri)) {
|
||||
// No Cookies for requested URI, authenticate user and add authentication header
|
||||
log.debug(
|
||||
"No Auth Cookie found for URI[%s]. Existing Cookies[%s] Authenticating... ",
|
||||
uri,
|
||||
cookieManager.getCookieStore().getCookies()
|
||||
);
|
||||
DruidKerberosUtil.authenticateIfRequired(config);
|
||||
UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
|
||||
String challenge = currentUser.doAs(new PrivilegedExceptionAction<String>()
|
||||
{
|
||||
@Override
|
||||
public String run() throws Exception
|
||||
{
|
||||
return DruidKerberosUtil.kerberosChallenge(host);
|
||||
}
|
||||
});
|
||||
request.setHeader(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challenge);
|
||||
should_retry_on_unauthorized_response = false;
|
||||
} else {
|
||||
should_retry_on_unauthorized_response = true;
|
||||
log.debug("Found Auth Cookie found for URI[%s].", uri);
|
||||
}
|
||||
|
||||
ListenableFuture<RetryResponseHolder<Final>> internalFuture = delegate.go(
|
||||
request,
|
||||
new RetryIfUnauthorizedResponseHandler<Intermediate, Final>(new ResponseCookieHandler(
|
||||
request.getUrl().toURI(),
|
||||
cookieManager,
|
||||
httpResponseHandler
|
||||
)),
|
||||
duration
|
||||
);
|
||||
|
||||
Futures.addCallback(internalFuture, new FutureCallback<RetryResponseHolder<Final>>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(RetryResponseHolder<Final> result)
|
||||
{
|
||||
if (should_retry_on_unauthorized_response && result.shouldRetry()) {
|
||||
log.info("Preparing for Retry");
|
||||
// remove Auth cookie
|
||||
DruidKerberosUtil.removeAuthCookie(cookieManager.getCookieStore(), uri);
|
||||
// clear existing cookie
|
||||
request.setHeader("Cookie", "");
|
||||
inner_go(request.copy(), httpResponseHandler, duration, future);
|
||||
} else {
|
||||
log.info("Not retrying and returning future response");
|
||||
future.set(result.getObj());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
future.setException(t);
|
||||
}
|
||||
}, exec);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provider;
|
||||
import com.metamx.http.client.HttpClient;
|
||||
import io.druid.guice.http.AbstractHttpClientProvider;
|
||||
|
||||
public class KerberosHttpClientProvider extends AbstractHttpClientProvider<HttpClient>
|
||||
{
|
||||
private final Provider<HttpClient> delegateProvider;
|
||||
private AuthenticationKerberosConfig config;
|
||||
|
||||
public KerberosHttpClientProvider(
|
||||
Provider<HttpClient> delegateProvider
|
||||
)
|
||||
{
|
||||
this.delegateProvider = delegateProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public void configure(Injector injector)
|
||||
{
|
||||
if (delegateProvider instanceof AbstractHttpClientProvider) {
|
||||
((AbstractHttpClientProvider) delegateProvider).configure(injector);
|
||||
}
|
||||
config = injector.getInstance(AuthenticationKerberosConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpClient get()
|
||||
{
|
||||
return new KerberosHttpClient(delegateProvider.get(), config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Provider;
|
||||
import io.druid.guice.http.AbstractHttpClientProvider;
|
||||
import io.druid.java.util.common.logger.Logger;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.Authentication;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
public class KerberosJettyHttpClientProvider extends AbstractHttpClientProvider<HttpClient>
|
||||
{
|
||||
private static final Logger log = new Logger(KerberosJettyHttpClientProvider.class);
|
||||
|
||||
private final Provider<HttpClient> delegateProvider;
|
||||
private AuthenticationKerberosConfig config;
|
||||
|
||||
|
||||
public KerberosJettyHttpClientProvider(
|
||||
Provider<HttpClient> delegateProvider
|
||||
)
|
||||
{
|
||||
this.delegateProvider = delegateProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public void configure(Injector injector)
|
||||
{
|
||||
if (delegateProvider instanceof AbstractHttpClientProvider) {
|
||||
((AbstractHttpClientProvider) delegateProvider).configure(injector);
|
||||
}
|
||||
config = injector.getInstance(AuthenticationKerberosConfig.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HttpClient get()
|
||||
{
|
||||
final HttpClient httpClient = delegateProvider.get();
|
||||
httpClient.getAuthenticationStore().addAuthentication(new Authentication()
|
||||
{
|
||||
@Override
|
||||
public boolean matches(String type, URI uri, String realm)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result authenticate(
|
||||
final Request request, ContentResponse response, Authentication.HeaderInfo headerInfo, Attributes context
|
||||
)
|
||||
{
|
||||
return new Result()
|
||||
{
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
return request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Request request)
|
||||
{
|
||||
try {
|
||||
// No need to set cookies as they are handled by Jetty Http Client itself.
|
||||
URI uri = request.getURI();
|
||||
if (DruidKerberosUtil.needToSendCredentials(httpClient.getCookieStore(), uri)) {
|
||||
log.debug(
|
||||
"No Auth Cookie found for URI[%s]. Existing Cookies[%s] Authenticating... ",
|
||||
uri,
|
||||
httpClient.getCookieStore().getCookies()
|
||||
);
|
||||
final String host = request.getHost();
|
||||
DruidKerberosUtil.authenticateIfRequired(config);
|
||||
UserGroupInformation currentUser = UserGroupInformation.getCurrentUser();
|
||||
String challenge = currentUser.doAs(new PrivilegedExceptionAction<String>()
|
||||
{
|
||||
@Override
|
||||
public String run() throws Exception
|
||||
{
|
||||
return DruidKerberosUtil.kerberosChallenge(host);
|
||||
}
|
||||
});
|
||||
request.getHeaders().add(HttpHeaders.Names.AUTHORIZATION, "Negotiate " + challenge);
|
||||
} else {
|
||||
log.debug("Found Auth Cookie found for URI[%s].", uri);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
return httpClient;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.metamx.http.client.response.ClientResponse;
|
||||
import com.metamx.http.client.response.HttpResponseHandler;
|
||||
import io.druid.java.util.common.logger.Logger;
|
||||
import org.jboss.netty.handler.codec.http.HttpChunk;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
public class ResponseCookieHandler<Intermediate, Final> implements HttpResponseHandler<Intermediate, Final>
|
||||
{
|
||||
private static final Logger log = new Logger(ResponseCookieHandler.class);
|
||||
|
||||
private final URI uri;
|
||||
private final CookieManager manager;
|
||||
private final HttpResponseHandler<Intermediate, Final> delegate;
|
||||
|
||||
public ResponseCookieHandler(URI uri, CookieManager manager, HttpResponseHandler<Intermediate, Final> delegate)
|
||||
{
|
||||
this.uri = uri;
|
||||
this.manager = manager;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<Intermediate> handleResponse(HttpResponse httpResponse)
|
||||
{
|
||||
try {
|
||||
final HttpHeaders headers = httpResponse.headers();
|
||||
manager.put(uri, Maps.asMap(headers.names(), new Function<String, List<String>>()
|
||||
{
|
||||
@Override
|
||||
public List<String> apply(String input)
|
||||
{
|
||||
return headers.getAll(input);
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error(e, "Error while processing Cookies from header");
|
||||
}
|
||||
finally {
|
||||
return delegate.handleResponse(httpResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<Intermediate> handleChunk(
|
||||
ClientResponse<Intermediate> clientResponse, HttpChunk httpChunk
|
||||
)
|
||||
{
|
||||
return delegate.handleChunk(clientResponse, httpChunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<Final> done(ClientResponse<Intermediate> clientResponse)
|
||||
{
|
||||
return delegate.done(clientResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ClientResponse<Intermediate> clientResponse, Throwable throwable)
|
||||
{
|
||||
delegate.exceptionCaught(clientResponse, throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.metamx.http.client.response.ClientResponse;
|
||||
import com.metamx.http.client.response.HttpResponseHandler;
|
||||
import io.druid.java.util.common.logger.Logger;
|
||||
import org.jboss.netty.handler.codec.http.HttpChunk;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
public class RetryIfUnauthorizedResponseHandler<Intermediate, Final>
|
||||
implements HttpResponseHandler<RetryResponseHolder<Intermediate>, RetryResponseHolder<Final>>
|
||||
{
|
||||
private static final Logger log = new Logger(RetryIfUnauthorizedResponseHandler.class);
|
||||
private final HttpResponseHandler<Intermediate, Final> httpResponseHandler;
|
||||
|
||||
|
||||
public RetryIfUnauthorizedResponseHandler(
|
||||
HttpResponseHandler<Intermediate, Final> httpResponseHandler
|
||||
)
|
||||
{
|
||||
this.httpResponseHandler = httpResponseHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<RetryResponseHolder<Intermediate>> handleResponse(HttpResponse httpResponse)
|
||||
{
|
||||
log.debug("UnauthorizedResponseHandler - Got response status [%s]", httpResponse.getStatus());
|
||||
if (httpResponse.getStatus().equals(HttpResponseStatus.UNAUTHORIZED)) {
|
||||
// Drain the buffer
|
||||
httpResponse.getContent().toString();
|
||||
return ClientResponse.unfinished(RetryResponseHolder.<Intermediate>retry());
|
||||
} else {
|
||||
return wrap(httpResponseHandler.handleResponse(httpResponse));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<RetryResponseHolder<Intermediate>> handleChunk(
|
||||
ClientResponse<RetryResponseHolder<Intermediate>> clientResponse, HttpChunk httpChunk
|
||||
)
|
||||
{
|
||||
if (clientResponse.getObj().shouldRetry()) {
|
||||
httpChunk.getContent().toString();
|
||||
return clientResponse;
|
||||
} else {
|
||||
return wrap(httpResponseHandler.handleChunk(unwrap(clientResponse), httpChunk));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientResponse<RetryResponseHolder<Final>> done(ClientResponse<RetryResponseHolder<Intermediate>> clientResponse)
|
||||
{
|
||||
if (clientResponse.getObj().shouldRetry()) {
|
||||
return ClientResponse.finished(RetryResponseHolder.<Final>retry());
|
||||
} else {
|
||||
return wrap(httpResponseHandler.done(unwrap(clientResponse)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ClientResponse<RetryResponseHolder<Intermediate>> clientResponse, Throwable throwable)
|
||||
{
|
||||
httpResponseHandler.exceptionCaught(unwrap(clientResponse), throwable);
|
||||
}
|
||||
|
||||
private <T> ClientResponse<RetryResponseHolder<T>> wrap(ClientResponse<T> response)
|
||||
{
|
||||
if (response.isFinished()) {
|
||||
return ClientResponse.finished(new RetryResponseHolder<T>(false, response.getObj()));
|
||||
} else {
|
||||
return ClientResponse.unfinished(new RetryResponseHolder<T>(false, response.getObj()));
|
||||
}
|
||||
}
|
||||
|
||||
private <T> ClientResponse<T> unwrap(ClientResponse<RetryResponseHolder<T>> response)
|
||||
{
|
||||
if (response.isFinished()) {
|
||||
return ClientResponse.finished(response.getObj().getObj());
|
||||
} else {
|
||||
return ClientResponse.unfinished(response.getObj().getObj());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
public class RetryResponseHolder<T>
|
||||
{
|
||||
private final boolean shouldRetry;
|
||||
private final T obj;
|
||||
|
||||
public RetryResponseHolder(boolean shouldRetry, T obj)
|
||||
{
|
||||
this.shouldRetry = shouldRetry;
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
public static <T> RetryResponseHolder<T> retry()
|
||||
{
|
||||
return new RetryResponseHolder<T>(true, null);
|
||||
}
|
||||
|
||||
public boolean shouldRetry()
|
||||
{
|
||||
return shouldRetry;
|
||||
}
|
||||
|
||||
public T getObj()
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SpnegoFilterConfig
|
||||
{
|
||||
|
||||
public static final List<String> DEFAULT_EXCLUDED_PATHS = Collections.emptyList();
|
||||
|
||||
@JsonProperty
|
||||
private final String principal;
|
||||
|
||||
@JsonProperty
|
||||
private final String keytab;
|
||||
|
||||
@JsonProperty
|
||||
private final String authToLocal;
|
||||
|
||||
@JsonProperty
|
||||
private final List<String> excludedPaths;
|
||||
|
||||
@JsonProperty
|
||||
private final String cookieSignatureSecret;
|
||||
|
||||
@JsonCreator
|
||||
public SpnegoFilterConfig(
|
||||
@JsonProperty("principal") String principal,
|
||||
@JsonProperty("keytab") String keytab,
|
||||
@JsonProperty("authToLocal") String authToLocal,
|
||||
@JsonProperty("excludedPaths") List<String> excludedPaths,
|
||||
@JsonProperty("cookieSignatureSecret") String cookieSignatureSecret
|
||||
)
|
||||
{
|
||||
this.principal = principal;
|
||||
this.keytab = keytab;
|
||||
this.authToLocal = authToLocal == null ? "DEFAULT" : authToLocal;
|
||||
this.excludedPaths = excludedPaths == null ? DEFAULT_EXCLUDED_PATHS : excludedPaths;
|
||||
this.cookieSignatureSecret = cookieSignatureSecret;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getPrincipal()
|
||||
{
|
||||
return principal;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getKeytab()
|
||||
{
|
||||
return keytab;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getAuthToLocal()
|
||||
{
|
||||
return authToLocal;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public List<String> getExcludedPaths()
|
||||
{
|
||||
return excludedPaths;
|
||||
}
|
||||
|
||||
@JsonProperty
|
||||
public String getCookieSignatureSecret()
|
||||
{
|
||||
return cookieSignatureSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SpnegoFilterConfig that = (SpnegoFilterConfig) o;
|
||||
|
||||
if (principal != null ? !principal.equals(that.principal) : that.principal != null) {
|
||||
return false;
|
||||
}
|
||||
if (keytab != null ? !keytab.equals(that.keytab) : that.keytab != null) {
|
||||
return false;
|
||||
}
|
||||
if (authToLocal != null ? !authToLocal.equals(that.authToLocal) : that.authToLocal != null) {
|
||||
return false;
|
||||
}
|
||||
if (excludedPaths != null ? !excludedPaths.equals(that.excludedPaths) : that.excludedPaths != null) {
|
||||
return false;
|
||||
}
|
||||
return cookieSignatureSecret != null
|
||||
? cookieSignatureSecret.equals(that.cookieSignatureSecret)
|
||||
: that.cookieSignatureSecret == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int result = principal != null ? principal.hashCode() : 0;
|
||||
result = 31 * result + (keytab != null ? keytab.hashCode() : 0);
|
||||
result = 31 * result + (authToLocal != null ? authToLocal.hashCode() : 0);
|
||||
result = 31 * result + (excludedPaths != null ? excludedPaths.hashCode() : 0);
|
||||
result = 31 * result + (cookieSignatureSecret != null ? cookieSignatureSecret.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.inject.Inject;
|
||||
import io.druid.guice.annotations.Self;
|
||||
import io.druid.server.DruidNode;
|
||||
import io.druid.server.initialization.jetty.ServletFilterHolder;
|
||||
import org.apache.hadoop.security.SecurityUtil;
|
||||
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SpnegoFilterHolder implements ServletFilterHolder
|
||||
{
|
||||
private final SpnegoFilterConfig config;
|
||||
private final DruidNode node;
|
||||
|
||||
@Inject
|
||||
public SpnegoFilterHolder(SpnegoFilterConfig config, @Self DruidNode node)
|
||||
{
|
||||
this.config = config;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter()
|
||||
{
|
||||
return new AuthenticationFilter()
|
||||
{
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
ClassLoader prevLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
// AuthenticationHandler is created during Authenticationfilter.init using reflection with thread context class loader.
|
||||
// In case of druid since the class is actually loaded as an extension and filter init is done in main thread.
|
||||
// We need to set the classloader explicitly to extension class loader.
|
||||
Thread.currentThread().setContextClassLoader(AuthenticationFilter.class.getClassLoader());
|
||||
super.init(filterConfig);
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader(prevLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(
|
||||
ServletRequest request, ServletResponse response, FilterChain filterChain
|
||||
) throws IOException, ServletException
|
||||
{
|
||||
String path = ((HttpServletRequest) request).getRequestURI();
|
||||
if (isExcluded(path)) {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
super.doFilter(request, response, filterChain);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isExcluded(String path)
|
||||
{
|
||||
for (String excluded : config.getExcludedPaths()) {
|
||||
if (path.startsWith(excluded)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Filter> getFilterClass()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getInitParameters()
|
||||
{
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
try {
|
||||
params.put(
|
||||
"kerberos.principal",
|
||||
SecurityUtil.getServerPrincipal(config.getPrincipal(), node.getHost())
|
||||
);
|
||||
params.put("kerberos.keytab", config.getKeytab());
|
||||
params.put(AuthenticationFilter.AUTH_TYPE, "kerberos");
|
||||
params.put("kerberos.name.rules", config.getAuthToLocal());
|
||||
if (config.getCookieSignatureSecret() != null) {
|
||||
params.put("signature.secret", config.getCookieSignatureSecret());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath()
|
||||
{
|
||||
return "/*";
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<DispatcherType> getDispatcherType()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.druid.security.kerberos.DruidKerberosModule
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provides;
|
||||
import io.druid.guice.ConfigModule;
|
||||
import io.druid.guice.DruidGuiceExtensions;
|
||||
import io.druid.guice.JsonConfigProvider;
|
||||
import io.druid.guice.LazySingleton;
|
||||
import io.druid.guice.PropertiesModule;
|
||||
import io.druid.jackson.DefaultObjectMapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
public class AuthenticationKerberosConfigTest
|
||||
{
|
||||
@Test
|
||||
public void testserde()
|
||||
{
|
||||
Injector injector = Guice.createInjector(
|
||||
new Module()
|
||||
{
|
||||
@Override
|
||||
public void configure(Binder binder)
|
||||
{
|
||||
binder.install(new PropertiesModule(Arrays.asList("test.runtime.properties")));
|
||||
binder.install(new ConfigModule());
|
||||
binder.install(new DruidGuiceExtensions());
|
||||
JsonConfigProvider.bind(binder, "druid.hadoop.security.kerberos", AuthenticationKerberosConfig.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@LazySingleton
|
||||
public ObjectMapper jsonMapper()
|
||||
{
|
||||
return new DefaultObjectMapper();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Properties props = injector.getInstance(Properties.class);
|
||||
AuthenticationKerberosConfig config = injector.getInstance(AuthenticationKerberosConfig.class);
|
||||
|
||||
Assert.assertEquals(props.getProperty("druid.hadoop.security.kerberos.principal"), config.getPrincipal());
|
||||
Assert.assertEquals(props.getProperty("druid.hadoop.security.kerberos.keytab"), config.getKeytab());
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Metamarkets 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 io.druid.security.kerberos;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provides;
|
||||
import io.druid.guice.ConfigModule;
|
||||
import io.druid.guice.DruidGuiceExtensions;
|
||||
import io.druid.guice.JsonConfigProvider;
|
||||
import io.druid.guice.LazySingleton;
|
||||
import io.druid.guice.PropertiesModule;
|
||||
import io.druid.jackson.DefaultObjectMapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
public class SpnegoFilterConfigTest
|
||||
{
|
||||
@Test
|
||||
public void testserde()
|
||||
{
|
||||
Injector injector = Guice.createInjector(
|
||||
new Module()
|
||||
{
|
||||
@Override
|
||||
public void configure(Binder binder)
|
||||
{
|
||||
binder.install(new PropertiesModule(Arrays.asList("test.runtime.properties")));
|
||||
binder.install(new ConfigModule());
|
||||
binder.install(new DruidGuiceExtensions());
|
||||
JsonConfigProvider.bind(binder, "druid.hadoop.security.spnego", SpnegoFilterConfig.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@LazySingleton
|
||||
public ObjectMapper jsonMapper()
|
||||
{
|
||||
return new DefaultObjectMapper();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Properties props = injector.getInstance(Properties.class);
|
||||
SpnegoFilterConfig config = injector.getInstance(SpnegoFilterConfig.class);
|
||||
|
||||
Assert.assertEquals(props.getProperty("druid.hadoop.security.spnego.principal"), config.getPrincipal());
|
||||
Assert.assertEquals(props.getProperty("druid.hadoop.security.spnego.keytab"), config.getKeytab());
|
||||
Assert.assertEquals(props.getProperty("druid.hadoop.security.spnego.authToLocal"), config.getAuthToLocal());
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
druid.hadoop.security.kerberos.principal=testPrincipal
|
||||
druid.hadoop.security.kerberos.keytab=testKeytab
|
||||
druid.hadoop.security.spnego.principal=spnegoPrincipal
|
||||
druid.hadoop.security.spnego.keytab=spnegoKeytab
|
||||
druid.hadoop.security.spnego.authToLocal=testAuthToLocal
|
||||
|
||||
|
|
@ -30,20 +30,16 @@ import com.metamx.emitter.core.LoggingEmitterConfig;
|
|||
import com.metamx.emitter.service.ServiceEmitter;
|
||||
import com.metamx.http.client.CredentialedHttpClient;
|
||||
import com.metamx.http.client.HttpClient;
|
||||
import com.metamx.http.client.HttpClientConfig;
|
||||
import com.metamx.http.client.HttpClientInit;
|
||||
import com.metamx.http.client.auth.BasicCredentials;
|
||||
import io.druid.curator.CuratorConfig;
|
||||
import io.druid.guice.JsonConfigProvider;
|
||||
import io.druid.guice.LazySingleton;
|
||||
import io.druid.guice.ManageLifecycle;
|
||||
import io.druid.guice.http.DruidHttpClientConfig;
|
||||
import io.druid.guice.annotations.Client;
|
||||
import io.druid.testing.IntegrationTestingConfig;
|
||||
import io.druid.testing.IntegrationTestingConfigProvider;
|
||||
import io.druid.testing.IntegrationTestingCuratorConfig;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class DruidTestModule implements Module
|
||||
|
@ -63,24 +59,16 @@ public class DruidTestModule implements Module
|
|||
@TestClient
|
||||
public HttpClient getHttpClient(
|
||||
IntegrationTestingConfig config,
|
||||
DruidHttpClientConfig httpClientConfig,
|
||||
Lifecycle lifecycle
|
||||
Lifecycle lifecycle,
|
||||
@Client HttpClient delegate
|
||||
)
|
||||
throws Exception
|
||||
{
|
||||
|
||||
final HttpClientConfig.Builder builder = HttpClientConfig
|
||||
.builder()
|
||||
.withNumConnections(httpClientConfig.getNumConnections())
|
||||
.withReadTimeout(httpClientConfig.getReadTimeout())
|
||||
.withWorkerCount(httpClientConfig.getNumMaxThreads());
|
||||
|
||||
builder.withSslContext(SSLContext.getDefault());
|
||||
HttpClient delegate = HttpClientInit.createClient(builder.build(), lifecycle);
|
||||
if (config.getUsername() != null) {
|
||||
return new CredentialedHttpClient(new BasicCredentials(config.getUsername(), config.getPassword()), delegate);
|
||||
} else {
|
||||
return delegate;
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -95,6 +95,7 @@
|
|||
<!-- Core extensions -->
|
||||
<module>extensions-core/avro-extensions</module>
|
||||
<module>extensions-core/datasketches</module>
|
||||
<module>extensions-core/druid-kerberos</module>
|
||||
<module>extensions-core/hdfs-storage</module>
|
||||
<module>extensions-core/histogram</module>
|
||||
<module>extensions-core/stats</module>
|
||||
|
|
Loading…
Reference in New Issue