Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.druid.query.dimension.DimensionSpec;
import io.druid.query.groupby.GroupByQuery;
import io.druid.segment.QueryableIndex;
import io.druid.server.security.NoopEscalator;
import io.druid.server.security.AuthConfig;
import io.druid.server.security.AuthTestUtils;
import io.druid.sql.calcite.planner.DruidPlanner;
Expand Down Expand Up @@ -120,8 +121,8 @@ public void setup() throws Exception
CalciteTests.createExprMacroTable(),
plannerConfig,
new AuthConfig(),
AuthTestUtils.TEST_AUTHENTICATOR_MAPPER,
AuthTestUtils.TEST_AUTHORIZER_MAPPER,
new NoopEscalator(),
CalciteTests.getJsonMapper()
);
groupByQuery = GroupByQuery
Expand Down
22 changes: 14 additions & 8 deletions docs/content/configuration/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ layout: doc_page
|Property|Type|Description|Default|Required|
|--------|-----------|--------|--------|--------|
|`druid.auth.authenticationChain`|JSON List of Strings|List of Authenticator type names|["allowAll"]|no|
|`druid.auth.escalatedAuthenticator`|String|Type of the Authenticator that should be used for internal Druid communications. This Authenticator must be present in `druid.auth.authenticationChain`.|"allowAll"|no|
|`druid.escalator.type`|String|Type of the Escalator that should be used for internal Druid communications. This Escalator must use an authentication scheme that is supported by an Authenticator in `druid.auth.authenticationChain`.|"noop"|no|
|`druid.auth.authorizers`|JSON List of Strings|List of Authorizer type names |["allowAll"]|no|

## Enabling Authentication/Authorization
Expand All @@ -33,10 +33,14 @@ Druid includes a built-in Authenticator, used for the default unsecured configur

This built-in Authenticator authenticates all requests, and always directs them to an Authorizer named "allowAll". It is not intended to be used for anything other than the default unsecured configuration.

## Internal Authenticator
The `druid.auth.escalatedAuthenticator` property determines what authentication scheme should be used for internal Druid cluster communications (such as when a broker node communicates with historical nodes for query processing).
## Escalator
The `druid.escalator.type` property determines what authentication scheme should be used for internal Druid cluster communications (such as when a broker node communicates with historical nodes for query processing).

The Authenticator chosen for this property must also be present in `druid.auth.authenticationChain`.
The Escalator chosen for this property must use an authentication scheme that is supported by an Authenticator in `druid.auth.authenticationChain. Authenticator extension implementors must also provide a corresponding Escalator implementation if they intend to use a particular authentication scheme for internal Druid communications.

### Noop Escalator

This built-in default Escalator is intended for use only with the default AllowAll Authenticator and Authorizer.

## Authorizers
Authorization decisions are handled by an Authorizer. The `druid.auth.authorizers` property determines what Authorizer implementations will be active.
Expand All @@ -63,7 +67,7 @@ When `druid.auth.authenticationChain` is left empty or unspecified, Druid will c

When `druid.auth.authorizers` is left empty or unspecified, Druid will create a single AllowAll Authorizer named "allowAll".

The default value of `druid.auth.escalatedAuthenticator` is "allowAll" to match the default unsecured Authenticator/Authorizer configurations.
The default value of `druid.escalator.type` is "noop" to match the default unsecured Authenticator/Authorizer configurations.

## Authenticator to Authorizer Routing

Expand All @@ -77,15 +81,17 @@ Internal requests between Druid nodes (non-user initiated communications) need t

These requests should be run as an "internal system user", an identity that represents the Druid cluster itself, with full access permissions.

The details of how the internal system user is defined is left to Authorizer and Authenticator implementations.
The details of how the internal system user is defined is left to extension implementations.

### Authorizer Internal System User Handling

Authorizers implementations must recognize and authorize an identity for the "internal system user", with full access permissions.

### Authenticator Internal System User Handling
### Authenticator and Escalator Internal System User Handling

An Authenticator implementation that is intended to support internal Druid communications must recognize credentials for the "internal system user", as provided by a corresponding Escalator implementation.

Authenticators must implement three methods related to the internal system user:
An Escalator must implement three methods related to the internal system user:

```java
public HttpClient createEscalatedClient(HttpClient baseClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public List<? extends Module> getJacksonModules()
{
return ImmutableList.of(
new SimpleModule("DruidKerberos").registerSubtypes(
KerberosAuthenticator.class
KerberosAuthenticator.class,
KerberosEscalator.class
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Throwables;
import com.metamx.http.client.HttpClient;
import io.druid.guice.annotations.Self;
import io.druid.java.util.common.StringUtils;
import io.druid.java.util.common.logger.Logger;
Expand All @@ -34,7 +33,6 @@
import io.druid.server.security.Authenticator;
import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.security.SecurityUtil;
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.server.AuthenticationFilter;
Expand All @@ -43,11 +41,6 @@
import org.apache.hadoop.security.authentication.util.Signer;
import org.apache.hadoop.security.authentication.util.SignerException;
import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
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 sun.security.krb5.EncryptedData;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.internal.APReq;
Expand Down Expand Up @@ -79,9 +72,7 @@
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
Expand Down Expand Up @@ -112,7 +103,6 @@ public class KerberosAuthenticator implements Authenticator
private final String authorizerName;
private LoginContext loginContext;


@JsonCreator
public KerberosAuthenticator(
@JsonProperty("serverPrincipal") String serverPrincipal,
Expand Down Expand Up @@ -440,80 +430,6 @@ public AuthenticationResult authenticateJDBCContext(Map<String, Object> context)
throw new UnsupportedOperationException("JDBC Kerberos auth not supported yet");
}

@Override
public HttpClient createEscalatedClient(HttpClient baseClient)
{
return new KerberosHttpClient(baseClient, internalClientPrincipal, internalClientKeytab);
}

@Override
public org.eclipse.jetty.client.HttpClient createEscalatedJettyClient(org.eclipse.jetty.client.HttpClient baseClient)
{
baseClient.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(baseClient.getCookieStore(), uri)) {
log.debug(
"No Auth Cookie found for URI[%s]. Existing Cookies[%s] Authenticating... ",
uri,
baseClient.getCookieStore().getCookies()
);
final String host = request.getHost();
DruidKerberosUtil.authenticateIfRequired(internalClientPrincipal, internalClientKeytab);
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 baseClient;
}

@Override
public AuthenticationResult createEscalatedAuthenticationResult()
{
return new AuthenticationResult(internalClientPrincipal, authorizerName, null);
}

private boolean isExcluded(String path)
{
for (String excluded : excludedPaths) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Throwables;
import com.metamx.http.client.HttpClient;
import io.druid.java.util.common.logger.Logger;
import io.druid.server.security.AuthenticationResult;
import io.druid.server.security.Escalator;
import org.apache.hadoop.security.UserGroupInformation;
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;

@JsonTypeName("kerberos")
public class KerberosEscalator implements Escalator
{
private static final Logger log = new Logger(KerberosAuthenticator.class);

private final String internalClientPrincipal;
private final String internalClientKeytab;
private final String authorizerName;

@JsonCreator
public KerberosEscalator(
@JsonProperty("authorizerName") String authorizerName,
@JsonProperty("internalClientPrincipal") String internalClientPrincipal,
@JsonProperty("internalClientKeytab") String internalClientKeytab
)
{
this.authorizerName = authorizerName;
this.internalClientPrincipal = internalClientPrincipal;
this.internalClientKeytab = internalClientKeytab;

}
@Override
public HttpClient createEscalatedClient(HttpClient baseClient)
{
return new KerberosHttpClient(baseClient, internalClientPrincipal, internalClientKeytab);
}

@Override
public org.eclipse.jetty.client.HttpClient createEscalatedJettyClient(org.eclipse.jetty.client.HttpClient baseClient)
{
baseClient.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(baseClient.getCookieStore(), uri)) {
log.debug(
"No Auth Cookie found for URI[%s]. Existing Cookies[%s] Authenticating... ",
uri,
baseClient.getCookieStore().getCookies()
);
final String host = request.getHost();
DruidKerberosUtil.authenticateIfRequired(internalClientPrincipal, internalClientKeytab);
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 baseClient;
}

@Override
public AuthenticationResult createEscalatedAuthenticationResult()
{
return new AuthenticationResult(internalClientPrincipal, authorizerName, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import io.druid.segment.column.ValueType;
import io.druid.segment.incremental.IncrementalIndexSchema;
import io.druid.segment.virtual.ExpressionVirtualColumn;
import io.druid.server.security.NoopEscalator;
import io.druid.server.security.AuthConfig;
import io.druid.server.security.AuthTestUtils;
import io.druid.sql.calcite.filtration.Filtration;
Expand Down Expand Up @@ -136,8 +137,8 @@ public void setUp() throws Exception
CalciteTests.createExprMacroTable(),
plannerConfig,
new AuthConfig(),
AuthTestUtils.TEST_AUTHENTICATOR_MAPPER,
AuthTestUtils.TEST_AUTHORIZER_MAPPER,
new NoopEscalator(),
CalciteTests.getJsonMapper()
);
}
Expand Down
Loading