HADOOP-10770. KMS add delegation token support. (tucu)

Conflicts:
	hadoop-common-project/hadoop-common/CHANGES.txt

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1619550 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Alejandro Abdelnur 2014-08-21 19:00:01 +00:00
parent 59c16d7947
commit 4dea3e8192
14 changed files with 418 additions and 145 deletions

View File

@ -125,6 +125,8 @@ Release 2.6.0 - UNRELEASED
HADOOP-10881. Clarify usage of encryption and encrypted encryption HADOOP-10881. Clarify usage of encryption and encrypted encryption
key in KeyProviderCryptoExtension. (wang) key in KeyProviderCryptoExtension. (wang)
HADOOP-10770. KMS add delegation token support. (tucu)
OPTIMIZATIONS OPTIMIZATIONS
HADOOP-10838. Byte array native checksumming. (James Thomas via todd) HADOOP-10838. Byte array native checksumming. (James Thomas via todd)

View File

@ -20,6 +20,8 @@ package org.apache.hadoop.crypto.key;
import org.apache.hadoop.security.Credentials; import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import java.io.IOException;
/** /**
* A KeyProvider extension with the ability to add a renewer's Delegation * A KeyProvider extension with the ability to add a renewer's Delegation
* Tokens to the provided Credentials. * Tokens to the provided Credentials.
@ -45,9 +47,10 @@ public class KeyProviderDelegationTokenExtension extends
* @param renewer the user allowed to renew the delegation tokens * @param renewer the user allowed to renew the delegation tokens
* @param credentials cache in which to add new delegation tokens * @param credentials cache in which to add new delegation tokens
* @return list of new delegation tokens * @return list of new delegation tokens
* @throws IOException thrown if IOException if an IO error occurs.
*/ */
public Token<?>[] addDelegationTokens(final String renewer, public Token<?>[] addDelegationTokens(final String renewer,
Credentials credentials); Credentials credentials) throws IOException;
} }
/** /**
@ -76,9 +79,10 @@ public class KeyProviderDelegationTokenExtension extends
* @param renewer the user allowed to renew the delegation tokens * @param renewer the user allowed to renew the delegation tokens
* @param credentials cache in which to add new delegation tokens * @param credentials cache in which to add new delegation tokens
* @return list of new delegation tokens * @return list of new delegation tokens
* @throws IOException thrown if IOException if an IO error occurs.
*/ */
public Token<?>[] addDelegationTokens(final String renewer, public Token<?>[] addDelegationTokens(final String renewer,
Credentials credentials) { Credentials credentials) throws IOException {
return getExtension().addDelegationTokens(renewer, credentials); return getExtension().addDelegationTokens(renewer, credentials);
} }

View File

@ -22,15 +22,17 @@ import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.KeyProvider; import org.apache.hadoop.crypto.key.KeyProvider;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion;
import org.apache.hadoop.crypto.key.KeyProviderDelegationTokenExtension;
import org.apache.hadoop.crypto.key.KeyProviderFactory; import org.apache.hadoop.crypto.key.KeyProviderFactory;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.ProviderUtils; import org.apache.hadoop.security.ProviderUtils;
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authentication.client.ConnectionConfigurator; import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
import org.apache.hadoop.security.authentication.client.PseudoAuthenticator;
import org.apache.hadoop.security.ssl.SSLFactory; import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
@ -69,7 +71,10 @@ import com.google.common.base.Preconditions;
* KMS client <code>KeyProvider</code> implementation. * KMS client <code>KeyProvider</code> implementation.
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
public class KMSClientProvider extends KeyProvider implements CryptoExtension { public class KMSClientProvider extends KeyProvider implements CryptoExtension,
KeyProviderDelegationTokenExtension.DelegationTokenExtension {
public static final String TOKEN_KIND = "kms-dt";
public static final String SCHEME_NAME = "kms"; public static final String SCHEME_NAME = "kms";
@ -229,6 +234,7 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension {
private String kmsUrl; private String kmsUrl;
private SSLFactory sslFactory; private SSLFactory sslFactory;
private ConnectionConfigurator configurator; private ConnectionConfigurator configurator;
private DelegationTokenAuthenticatedURL.Token authToken;
@Override @Override
public String toString() { public String toString() {
@ -309,6 +315,7 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension {
CommonConfigurationKeysPublic. CommonConfigurationKeysPublic.
KMS_CLIENT_ENC_KEY_CACHE_NUM_REFILL_THREADS_DEFAULT), KMS_CLIENT_ENC_KEY_CACHE_NUM_REFILL_THREADS_DEFAULT),
new EncryptedQueueRefiller()); new EncryptedQueueRefiller());
authToken = new DelegationTokenAuthenticatedURL.Token();
} }
private String createServiceURL(URL url) throws IOException { private String createServiceURL(URL url) throws IOException {
@ -325,12 +332,14 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension {
try { try {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(kmsUrl); sb.append(kmsUrl);
sb.append(collection); if (collection != null) {
if (resource != null) { sb.append(collection);
sb.append("/").append(URLEncoder.encode(resource, UTF8)); if (resource != null) {
} sb.append("/").append(URLEncoder.encode(resource, UTF8));
if (subResource != null) { if (subResource != null) {
sb.append("/").append(subResource); sb.append("/").append(subResource);
}
}
} }
URIBuilder uriBuilder = new URIBuilder(sb.toString()); URIBuilder uriBuilder = new URIBuilder(sb.toString());
if (parameters != null) { if (parameters != null) {
@ -369,9 +378,9 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension {
throws IOException { throws IOException {
HttpURLConnection conn; HttpURLConnection conn;
try { try {
AuthenticatedURL authUrl = new AuthenticatedURL(new PseudoAuthenticator(), DelegationTokenAuthenticatedURL authUrl =
configurator); new DelegationTokenAuthenticatedURL(configurator);
conn = authUrl.openConnection(url, new AuthenticatedURL.Token()); conn = authUrl.openConnection(url, authToken);
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
throw new IOException(ex); throw new IOException(ex);
} }
@ -729,4 +738,25 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension {
} }
} }
@Override
public Token<?>[] addDelegationTokens(String renewer,
Credentials credentials) throws IOException {
Token<?>[] tokens;
URL url = createURL(null, null, null, null);
DelegationTokenAuthenticatedURL authUrl =
new DelegationTokenAuthenticatedURL(configurator);
try {
Token<?> token = authUrl.getDelegationToken(url, authToken, renewer);
if (token != null) {
credentials.addToken(token.getService(), token);
tokens = new Token<?>[] { token };
} else {
throw new IOException("Got NULL as delegation token");
}
} catch (AuthenticationException ex) {
throw new IOException(ex);
}
return tokens;
}
} }

View File

@ -75,7 +75,7 @@ public abstract class DelegationTokenAuthenticationHandler
public static final String PREFIX = "delegation-token."; public static final String PREFIX = "delegation-token.";
public static final String TOKEN_KIND = PREFIX + "token-kind.sec"; public static final String TOKEN_KIND = PREFIX + "token-kind";
public static final String UPDATE_INTERVAL = PREFIX + "update-interval.sec"; public static final String UPDATE_INTERVAL = PREFIX + "update-interval.sec";
public static final long UPDATE_INTERVAL_DEFAULT = 24 * 60 * 60; public static final long UPDATE_INTERVAL_DEFAULT = 24 * 60 * 60;

View File

@ -25,9 +25,10 @@ import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion;
import org.apache.hadoop.crypto.key.kms.KMSRESTConstants; import org.apache.hadoop.crypto.key.kms.KMSRESTConstants;
import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.security.authorize.AuthorizationException;
import org.apache.hadoop.crypto.key.kms.KMSClientProvider; import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
import org.apache.hadoop.security.token.delegation.web.HttpUserGroupInformation;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
@ -38,15 +39,13 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.Principal; import java.security.PrivilegedExceptionAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -74,15 +73,6 @@ public class KMS {
kmsAudit= KMSWebApp.getKMSAudit(); kmsAudit= KMSWebApp.getKMSAudit();
} }
private static Principal getPrincipal(SecurityContext securityContext)
throws AuthenticationException{
Principal user = securityContext.getUserPrincipal();
if (user == null) {
throw new AuthenticationException("User must be authenticated");
}
return user;
}
private static final String UNAUTHORIZED_MSG_WITH_KEY = private static final String UNAUTHORIZED_MSG_WITH_KEY =
"User:%s not allowed to do '%s' on '%s'"; "User:%s not allowed to do '%s' on '%s'";
@ -90,20 +80,21 @@ public class KMS {
private static final String UNAUTHORIZED_MSG_WITHOUT_KEY = private static final String UNAUTHORIZED_MSG_WITHOUT_KEY =
"User:%s not allowed to do '%s'"; "User:%s not allowed to do '%s'";
private void assertAccess(KMSACLs.Type aclType, Principal principal, private void assertAccess(KMSACLs.Type aclType, UserGroupInformation ugi,
KMSOp operation) throws AccessControlException { KMSOp operation) throws AccessControlException {
assertAccess(aclType, principal, operation, null); assertAccess(aclType, ugi, operation, null);
} }
private void assertAccess(KMSACLs.Type aclType, Principal principal, private void assertAccess(KMSACLs.Type aclType,
KMSOp operation, String key) throws AccessControlException { UserGroupInformation ugi, KMSOp operation, String key)
if (!KMSWebApp.getACLs().hasAccess(aclType, principal.getName())) { throws AccessControlException {
if (!KMSWebApp.getACLs().hasAccess(aclType, ugi)) {
KMSWebApp.getUnauthorizedCallsMeter().mark(); KMSWebApp.getUnauthorizedCallsMeter().mark();
kmsAudit.unauthorized(principal, operation, key); kmsAudit.unauthorized(ugi, operation, key);
throw new AuthorizationException(String.format( throw new AuthorizationException(String.format(
(key != null) ? UNAUTHORIZED_MSG_WITH_KEY (key != null) ? UNAUTHORIZED_MSG_WITH_KEY
: UNAUTHORIZED_MSG_WITHOUT_KEY, : UNAUTHORIZED_MSG_WITHOUT_KEY,
principal.getName(), operation, key)); ugi.getShortUserName(), operation, key));
} }
} }
@ -123,15 +114,14 @@ public class KMS {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Response createKey(@Context SecurityContext securityContext, public Response createKey(Map jsonKey) throws Exception {
Map jsonKey) throws Exception {
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
String name = (String) jsonKey.get(KMSRESTConstants.NAME_FIELD); final String name = (String) jsonKey.get(KMSRESTConstants.NAME_FIELD);
KMSClientProvider.checkNotEmpty(name, KMSRESTConstants.NAME_FIELD); KMSClientProvider.checkNotEmpty(name, KMSRESTConstants.NAME_FIELD);
assertAccess(KMSACLs.Type.CREATE, user, KMSOp.CREATE_KEY, name); assertAccess(KMSACLs.Type.CREATE, user, KMSOp.CREATE_KEY, name);
String cipher = (String) jsonKey.get(KMSRESTConstants.CIPHER_FIELD); String cipher = (String) jsonKey.get(KMSRESTConstants.CIPHER_FIELD);
String material = (String) jsonKey.get(KMSRESTConstants.MATERIAL_FIELD); final String material = (String) jsonKey.get(KMSRESTConstants.MATERIAL_FIELD);
int length = (jsonKey.containsKey(KMSRESTConstants.LENGTH_FIELD)) int length = (jsonKey.containsKey(KMSRESTConstants.LENGTH_FIELD))
? (Integer) jsonKey.get(KMSRESTConstants.LENGTH_FIELD) : 0; ? (Integer) jsonKey.get(KMSRESTConstants.LENGTH_FIELD) : 0;
String description = (String) String description = (String)
@ -142,7 +132,7 @@ public class KMS {
assertAccess(KMSACLs.Type.SET_KEY_MATERIAL, user, assertAccess(KMSACLs.Type.SET_KEY_MATERIAL, user,
KMSOp.CREATE_KEY, name); KMSOp.CREATE_KEY, name);
} }
KeyProvider.Options options = new KeyProvider.Options( final KeyProvider.Options options = new KeyProvider.Options(
KMSWebApp.getConfiguration()); KMSWebApp.getConfiguration());
if (cipher != null) { if (cipher != null) {
options.setCipher(cipher); options.setCipher(cipher);
@ -153,16 +143,23 @@ public class KMS {
options.setDescription(description); options.setDescription(description);
options.setAttributes(attributes); options.setAttributes(attributes);
KeyProvider.KeyVersion keyVersion = (material != null) KeyProvider.KeyVersion keyVersion = user.doAs(
? provider.createKey(name, Base64.decodeBase64(material), options) new PrivilegedExceptionAction<KeyVersion>() {
: provider.createKey(name, options); @Override
public KeyVersion run() throws Exception {
provider.flush(); KeyProvider.KeyVersion keyVersion = (material != null)
? provider.createKey(name, Base64.decodeBase64(material), options)
: provider.createKey(name, options);
provider.flush();
return keyVersion;
}
}
);
kmsAudit.ok(user, KMSOp.CREATE_KEY, name, "UserProvidedMaterial:" + kmsAudit.ok(user, KMSOp.CREATE_KEY, name, "UserProvidedMaterial:" +
(material != null) + " Description:" + description); (material != null) + " Description:" + description);
if (!KMSWebApp.getACLs().hasAccess(KMSACLs.Type.GET, user.getName())) { if (!KMSWebApp.getACLs().hasAccess(KMSACLs.Type.GET, user)) {
keyVersion = removeKeyMaterial(keyVersion); keyVersion = removeKeyMaterial(keyVersion);
} }
Map json = KMSServerJSONUtils.toJSON(keyVersion); Map json = KMSServerJSONUtils.toJSON(keyVersion);
@ -176,14 +173,21 @@ public class KMS {
@DELETE @DELETE
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}") @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
public Response deleteKey(@Context SecurityContext securityContext, public Response deleteKey(@PathParam("name") final String name)
@PathParam("name") String name) throws Exception { throws Exception {
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
assertAccess(KMSACLs.Type.DELETE, user, KMSOp.DELETE_KEY, name); assertAccess(KMSACLs.Type.DELETE, user, KMSOp.DELETE_KEY, name);
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
provider.deleteKey(name);
provider.flush(); user.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
provider.deleteKey(name);
provider.flush();
return null;
}
});
kmsAudit.ok(user, KMSOp.DELETE_KEY, name, ""); kmsAudit.ok(user, KMSOp.DELETE_KEY, name, "");
@ -194,29 +198,36 @@ public class KMS {
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}") @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response rolloverKey(@Context SecurityContext securityContext, public Response rolloverKey(@PathParam("name") final String name,
@PathParam("name") String name, Map jsonMaterial) Map jsonMaterial) throws Exception {
throws Exception {
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
assertAccess(KMSACLs.Type.ROLLOVER, user, KMSOp.ROLL_NEW_VERSION, name); assertAccess(KMSACLs.Type.ROLLOVER, user, KMSOp.ROLL_NEW_VERSION, name);
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
String material = (String) final String material = (String)
jsonMaterial.get(KMSRESTConstants.MATERIAL_FIELD); jsonMaterial.get(KMSRESTConstants.MATERIAL_FIELD);
if (material != null) { if (material != null) {
assertAccess(KMSACLs.Type.SET_KEY_MATERIAL, user, assertAccess(KMSACLs.Type.SET_KEY_MATERIAL, user,
KMSOp.ROLL_NEW_VERSION, name); KMSOp.ROLL_NEW_VERSION, name);
} }
KeyProvider.KeyVersion keyVersion = (material != null)
? provider.rollNewVersion(name, Base64.decodeBase64(material))
: provider.rollNewVersion(name);
provider.flush(); KeyProvider.KeyVersion keyVersion = user.doAs(
new PrivilegedExceptionAction<KeyVersion>() {
@Override
public KeyVersion run() throws Exception {
KeyVersion keyVersion = (material != null)
? provider.rollNewVersion(name, Base64.decodeBase64(material))
: provider.rollNewVersion(name);
provider.flush();
return keyVersion;
}
}
);
kmsAudit.ok(user, KMSOp.ROLL_NEW_VERSION, name, "UserProvidedMaterial:" + kmsAudit.ok(user, KMSOp.ROLL_NEW_VERSION, name, "UserProvidedMaterial:" +
(material != null) + " NewVersion:" + keyVersion.getVersionName()); (material != null) + " NewVersion:" + keyVersion.getVersionName());
if (!KMSWebApp.getACLs().hasAccess(KMSACLs.Type.GET, user.getName())) { if (!KMSWebApp.getACLs().hasAccess(KMSACLs.Type.GET, user)) {
keyVersion = removeKeyMaterial(keyVersion); keyVersion = removeKeyMaterial(keyVersion);
} }
Map json = KMSServerJSONUtils.toJSON(keyVersion); Map json = KMSServerJSONUtils.toJSON(keyVersion);
@ -226,14 +237,23 @@ public class KMS {
@GET @GET
@Path(KMSRESTConstants.KEYS_METADATA_RESOURCE) @Path(KMSRESTConstants.KEYS_METADATA_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getKeysMetadata(@Context SecurityContext securityContext, public Response getKeysMetadata(@QueryParam(KMSRESTConstants.KEY)
@QueryParam(KMSRESTConstants.KEY) List<String> keyNamesList) List<String> keyNamesList) throws Exception {
throws Exception {
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
String[] keyNames = keyNamesList.toArray(new String[keyNamesList.size()]); final String[] keyNames = keyNamesList.toArray(
new String[keyNamesList.size()]);
assertAccess(KMSACLs.Type.GET_METADATA, user, KMSOp.GET_KEYS_METADATA); assertAccess(KMSACLs.Type.GET_METADATA, user, KMSOp.GET_KEYS_METADATA);
KeyProvider.Metadata[] keysMeta = provider.getKeysMetadata(keyNames);
KeyProvider.Metadata[] keysMeta = user.doAs(
new PrivilegedExceptionAction<KeyProvider.Metadata[]>() {
@Override
public KeyProvider.Metadata[] run() throws Exception {
return provider.getKeysMetadata(keyNames);
}
}
);
Object json = KMSServerJSONUtils.toJSON(keyNames, keysMeta); Object json = KMSServerJSONUtils.toJSON(keyNames, keysMeta);
kmsAudit.ok(user, KMSOp.GET_KEYS_METADATA, ""); kmsAudit.ok(user, KMSOp.GET_KEYS_METADATA, "");
return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
@ -242,36 +262,52 @@ public class KMS {
@GET @GET
@Path(KMSRESTConstants.KEYS_NAMES_RESOURCE) @Path(KMSRESTConstants.KEYS_NAMES_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getKeyNames(@Context SecurityContext securityContext) public Response getKeyNames() throws Exception {
throws Exception {
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
assertAccess(KMSACLs.Type.GET_KEYS, user, KMSOp.GET_KEYS); assertAccess(KMSACLs.Type.GET_KEYS, user, KMSOp.GET_KEYS);
Object json = provider.getKeys();
List<String> json = user.doAs(
new PrivilegedExceptionAction<List<String>>() {
@Override
public List<String> run() throws Exception {
return provider.getKeys();
}
}
);
kmsAudit.ok(user, KMSOp.GET_KEYS, ""); kmsAudit.ok(user, KMSOp.GET_KEYS, "");
return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
} }
@GET @GET
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}") @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}")
public Response getKey(@Context SecurityContext securityContext, public Response getKey(@PathParam("name") String name)
@PathParam("name") String name)
throws Exception { throws Exception {
return getMetadata(securityContext, name); return getMetadata(name);
} }
@GET @GET
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" + @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" +
KMSRESTConstants.METADATA_SUB_RESOURCE) KMSRESTConstants.METADATA_SUB_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getMetadata(@Context SecurityContext securityContext, public Response getMetadata(@PathParam("name") final String name)
@PathParam("name") String name)
throws Exception { throws Exception {
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
KMSWebApp.getAdminCallsMeter().mark(); KMSWebApp.getAdminCallsMeter().mark();
assertAccess(KMSACLs.Type.GET_METADATA, user, KMSOp.GET_METADATA, name); assertAccess(KMSACLs.Type.GET_METADATA, user, KMSOp.GET_METADATA, name);
Object json = KMSServerJSONUtils.toJSON(name, provider.getMetadata(name));
KeyProvider.Metadata metadata = user.doAs(
new PrivilegedExceptionAction<KeyProvider.Metadata>() {
@Override
public KeyProvider.Metadata run() throws Exception {
return provider.getMetadata(name);
}
}
);
Object json = KMSServerJSONUtils.toJSON(name, metadata);
kmsAudit.ok(user, KMSOp.GET_METADATA, name, ""); kmsAudit.ok(user, KMSOp.GET_METADATA, name, "");
return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
} }
@ -280,14 +316,23 @@ public class KMS {
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" + @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" +
KMSRESTConstants.CURRENT_VERSION_SUB_RESOURCE) KMSRESTConstants.CURRENT_VERSION_SUB_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getCurrentVersion(@Context SecurityContext securityContext, public Response getCurrentVersion(@PathParam("name") final String name)
@PathParam("name") String name)
throws Exception { throws Exception {
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
KMSWebApp.getKeyCallsMeter().mark(); KMSWebApp.getKeyCallsMeter().mark();
assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_CURRENT_KEY, name); assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_CURRENT_KEY, name);
Object json = KMSServerJSONUtils.toJSON(provider.getCurrentKey(name));
KeyVersion keyVersion = user.doAs(
new PrivilegedExceptionAction<KeyVersion>() {
@Override
public KeyVersion run() throws Exception {
return provider.getCurrentKey(name);
}
}
);
Object json = KMSServerJSONUtils.toJSON(keyVersion);
kmsAudit.ok(user, KMSOp.GET_CURRENT_KEY, name, ""); kmsAudit.ok(user, KMSOp.GET_CURRENT_KEY, name, "");
return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
} }
@ -295,14 +340,22 @@ public class KMS {
@GET @GET
@Path(KMSRESTConstants.KEY_VERSION_RESOURCE + "/{versionName:.*}") @Path(KMSRESTConstants.KEY_VERSION_RESOURCE + "/{versionName:.*}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getKeyVersion(@Context SecurityContext securityContext, public Response getKeyVersion(
@PathParam("versionName") String versionName) @PathParam("versionName") final String versionName) throws Exception {
throws Exception { UserGroupInformation user = HttpUserGroupInformation.get();
Principal user = getPrincipal(securityContext);
KMSClientProvider.checkNotEmpty(versionName, "versionName"); KMSClientProvider.checkNotEmpty(versionName, "versionName");
KMSWebApp.getKeyCallsMeter().mark(); KMSWebApp.getKeyCallsMeter().mark();
KeyVersion keyVersion = provider.getKeyVersion(versionName);
assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_KEY_VERSION); assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_KEY_VERSION);
KeyVersion keyVersion = user.doAs(
new PrivilegedExceptionAction<KeyVersion>() {
@Override
public KeyVersion run() throws Exception {
return provider.getKeyVersion(versionName);
}
}
);
if (keyVersion != null) { if (keyVersion != null) {
kmsAudit.ok(user, KMSOp.GET_KEY_VERSION, keyVersion.getName(), ""); kmsAudit.ok(user, KMSOp.GET_KEY_VERSION, keyVersion.getName(), "");
} }
@ -316,13 +369,12 @@ public class KMS {
KMSRESTConstants.EEK_SUB_RESOURCE) KMSRESTConstants.EEK_SUB_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response generateEncryptedKeys( public Response generateEncryptedKeys(
@Context SecurityContext securityContext, @PathParam("name") final String name,
@PathParam("name") String name,
@QueryParam(KMSRESTConstants.EEK_OP) String edekOp, @QueryParam(KMSRESTConstants.EEK_OP) String edekOp,
@DefaultValue("1") @DefaultValue("1")
@QueryParam(KMSRESTConstants.EEK_NUM_KEYS) int numKeys) @QueryParam(KMSRESTConstants.EEK_NUM_KEYS) final int numKeys)
throws Exception { throws Exception {
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
KMSClientProvider.checkNotNull(edekOp, "eekOp"); KMSClientProvider.checkNotNull(edekOp, "eekOp");
@ -330,12 +382,22 @@ public class KMS {
if (edekOp.equals(KMSRESTConstants.EEK_GENERATE)) { if (edekOp.equals(KMSRESTConstants.EEK_GENERATE)) {
assertAccess(KMSACLs.Type.GENERATE_EEK, user, KMSOp.GENERATE_EEK, name); assertAccess(KMSACLs.Type.GENERATE_EEK, user, KMSOp.GENERATE_EEK, name);
List<EncryptedKeyVersion> retEdeks = final List<EncryptedKeyVersion> retEdeks =
new LinkedList<EncryptedKeyVersion>(); new LinkedList<EncryptedKeyVersion>();
try { try {
for (int i = 0; i < numKeys; i ++) {
retEdeks.add(provider.generateEncryptedKey(name)); user.doAs(
} new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
for (int i = 0; i < numKeys; i++) {
retEdeks.add(provider.generateEncryptedKey(name));
}
return null;
}
}
);
} catch (Exception e) { } catch (Exception e) {
throw new IOException(e); throw new IOException(e);
} }
@ -359,16 +421,17 @@ public class KMS {
@Path(KMSRESTConstants.KEY_VERSION_RESOURCE + "/{versionName:.*}/" + @Path(KMSRESTConstants.KEY_VERSION_RESOURCE + "/{versionName:.*}/" +
KMSRESTConstants.EEK_SUB_RESOURCE) KMSRESTConstants.EEK_SUB_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response decryptEncryptedKey(@Context SecurityContext securityContext, public Response decryptEncryptedKey(
@PathParam("versionName") String versionName, @PathParam("versionName") final String versionName,
@QueryParam(KMSRESTConstants.EEK_OP) String eekOp, @QueryParam(KMSRESTConstants.EEK_OP) String eekOp,
Map jsonPayload) Map jsonPayload)
throws Exception { throws Exception {
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
KMSClientProvider.checkNotEmpty(versionName, "versionName"); KMSClientProvider.checkNotEmpty(versionName, "versionName");
KMSClientProvider.checkNotNull(eekOp, "eekOp"); KMSClientProvider.checkNotNull(eekOp, "eekOp");
String keyName = (String) jsonPayload.get(KMSRESTConstants.NAME_FIELD); final String keyName = (String) jsonPayload.get(
KMSRESTConstants.NAME_FIELD);
String ivStr = (String) jsonPayload.get(KMSRESTConstants.IV_FIELD); String ivStr = (String) jsonPayload.get(KMSRESTConstants.IV_FIELD);
String encMaterialStr = String encMaterialStr =
(String) jsonPayload.get(KMSRESTConstants.MATERIAL_FIELD); (String) jsonPayload.get(KMSRESTConstants.MATERIAL_FIELD);
@ -376,14 +439,24 @@ public class KMS {
if (eekOp.equals(KMSRESTConstants.EEK_DECRYPT)) { if (eekOp.equals(KMSRESTConstants.EEK_DECRYPT)) {
assertAccess(KMSACLs.Type.DECRYPT_EEK, user, KMSOp.DECRYPT_EEK, keyName); assertAccess(KMSACLs.Type.DECRYPT_EEK, user, KMSOp.DECRYPT_EEK, keyName);
KMSClientProvider.checkNotNull(ivStr, KMSRESTConstants.IV_FIELD); KMSClientProvider.checkNotNull(ivStr, KMSRESTConstants.IV_FIELD);
byte[] iv = Base64.decodeBase64(ivStr); final byte[] iv = Base64.decodeBase64(ivStr);
KMSClientProvider.checkNotNull(encMaterialStr, KMSClientProvider.checkNotNull(encMaterialStr,
KMSRESTConstants.MATERIAL_FIELD); KMSRESTConstants.MATERIAL_FIELD);
byte[] encMaterial = Base64.decodeBase64(encMaterialStr); final byte[] encMaterial = Base64.decodeBase64(encMaterialStr);
KeyProvider.KeyVersion retKeyVersion =
provider.decryptEncryptedKey( KeyProvider.KeyVersion retKeyVersion = user.doAs(
new KMSClientProvider.KMSEncryptedKeyVersion(keyName, versionName, new PrivilegedExceptionAction<KeyVersion>() {
iv, KeyProviderCryptoExtension.EEK, encMaterial)); @Override
public KeyVersion run() throws Exception {
return provider.decryptEncryptedKey(
new KMSClientProvider.KMSEncryptedKeyVersion(keyName,
versionName, iv, KeyProviderCryptoExtension.EEK,
encMaterial)
);
}
}
);
retJSON = KMSServerJSONUtils.toJSON(retKeyVersion); retJSON = KMSServerJSONUtils.toJSON(retKeyVersion);
kmsAudit.ok(user, KMSOp.DECRYPT_EEK, keyName, ""); kmsAudit.ok(user, KMSOp.DECRYPT_EEK, keyName, "");
} else { } else {
@ -400,14 +473,23 @@ public class KMS {
@Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" + @Path(KMSRESTConstants.KEY_RESOURCE + "/{name:.*}/" +
KMSRESTConstants.VERSIONS_SUB_RESOURCE) KMSRESTConstants.VERSIONS_SUB_RESOURCE)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Response getKeyVersions(@Context SecurityContext securityContext, public Response getKeyVersions(@PathParam("name") final String name)
@PathParam("name") String name)
throws Exception { throws Exception {
Principal user = getPrincipal(securityContext); UserGroupInformation user = HttpUserGroupInformation.get();
KMSClientProvider.checkNotEmpty(name, "name"); KMSClientProvider.checkNotEmpty(name, "name");
KMSWebApp.getKeyCallsMeter().mark(); KMSWebApp.getKeyCallsMeter().mark();
assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_KEY_VERSIONS, name); assertAccess(KMSACLs.Type.GET, user, KMSOp.GET_KEY_VERSIONS, name);
Object json = KMSServerJSONUtils.toJSON(provider.getKeyVersions(name));
List<KeyVersion> ret = user.doAs(
new PrivilegedExceptionAction<List<KeyVersion>>() {
@Override
public List<KeyVersion> run() throws Exception {
return provider.getKeyVersions(name);
}
}
);
Object json = KMSServerJSONUtils.toJSON(ret);
kmsAudit.ok(user, KMSOp.GET_KEY_VERSIONS, name, ""); kmsAudit.ok(user, KMSOp.GET_KEY_VERSIONS, name, "");
return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build(); return Response.ok().type(MediaType.APPLICATION_JSON).entity(json).build();
} }

View File

@ -113,8 +113,7 @@ public class KMSACLs implements Runnable {
return conf; return conf;
} }
public boolean hasAccess(Type type, String user) { public boolean hasAccess(Type type, UserGroupInformation ugi) {
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(user);
return acls.get(type).isUserAllowed(ugi); return acls.get(type).isUserAllowed(ugi);
} }

View File

@ -17,6 +17,7 @@
*/ */
package org.apache.hadoop.crypto.key.kms.server; package org.apache.hadoop.crypto.key.kms.server;
import org.apache.hadoop.security.UserGroupInformation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -29,7 +30,6 @@ import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.security.Principal;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -186,22 +186,22 @@ public class KMSAudit {
} }
} }
public void ok(Principal user, KMS.KMSOp op, String key, public void ok(UserGroupInformation user, KMS.KMSOp op, String key,
String extraMsg) { String extraMsg) {
op(OpStatus.OK, op, user.getName(), key, extraMsg); op(OpStatus.OK, op, user.getShortUserName(), key, extraMsg);
} }
public void ok(Principal user, KMS.KMSOp op, String extraMsg) { public void ok(UserGroupInformation user, KMS.KMSOp op, String extraMsg) {
op(OpStatus.OK, op, user.getName(), null, extraMsg); op(OpStatus.OK, op, user.getShortUserName(), null, extraMsg);
} }
public void unauthorized(Principal user, KMS.KMSOp op, String key) { public void unauthorized(UserGroupInformation user, KMS.KMSOp op, String key) {
op(OpStatus.UNAUTHORIZED, op, user.getName(), key, ""); op(OpStatus.UNAUTHORIZED, op, user.getShortUserName(), key, "");
} }
public void error(Principal user, String method, String url, public void error(UserGroupInformation user, String method, String url,
String extraMsg) { String extraMsg) {
op(OpStatus.ERROR, null, user.getName(), null, "Method:'" + method op(OpStatus.ERROR, null, user.getShortUserName(), null, "Method:'" + method
+ "' Exception:'" + extraMsg + "'"); + "' Exception:'" + extraMsg + "'");
} }

View File

@ -19,7 +19,13 @@ package org.apache.hadoop.crypto.key.kms.server;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.authentication.server.AuthenticationFilter; import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationFilter;
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
import org.apache.hadoop.security.token.delegation.web.KerberosDelegationTokenAuthenticationHandler;
import org.apache.hadoop.security.token.delegation.web.PseudoDelegationTokenAuthenticationHandler;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@ -38,7 +44,8 @@ import java.util.Properties;
* file. * file.
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
public class KMSAuthenticationFilter extends AuthenticationFilter { public class KMSAuthenticationFilter
extends DelegationTokenAuthenticationFilter {
private static final String CONF_PREFIX = KMSConfiguration.CONFIG_PREFIX + private static final String CONF_PREFIX = KMSConfiguration.CONFIG_PREFIX +
"authentication."; "authentication.";
@ -55,6 +62,16 @@ public class KMSAuthenticationFilter extends AuthenticationFilter {
props.setProperty(name, value); props.setProperty(name, value);
} }
} }
String authType = props.getProperty(AUTH_TYPE);
if (authType.equals(PseudoAuthenticationHandler.TYPE)) {
props.setProperty(AUTH_TYPE,
PseudoDelegationTokenAuthenticationHandler.class.getName());
} else if (authType.equals(KerberosAuthenticationHandler.TYPE)) {
props.setProperty(AUTH_TYPE,
KerberosDelegationTokenAuthenticationHandler.class.getName());
}
props.setProperty(DelegationTokenAuthenticationHandler.TOKEN_KIND,
KMSClientProvider.TOKEN_KIND);
return props; return props;
} }

View File

@ -23,6 +23,7 @@ import com.sun.jersey.api.container.ContainerException;
import org.apache.hadoop.crypto.key.kms.KMSRESTConstants; import org.apache.hadoop.crypto.key.kms.KMSRESTConstants;
import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.security.authorize.AuthorizationException;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -34,7 +35,6 @@ import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import java.io.IOException; import java.io.IOException;
import java.security.Principal;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -102,7 +102,7 @@ public class KMSExceptionsProvider implements ExceptionMapper<Exception> {
status = Response.Status.INTERNAL_SERVER_ERROR; status = Response.Status.INTERNAL_SERVER_ERROR;
} }
if (doAudit) { if (doAudit) {
KMSWebApp.getKMSAudit().error(KMSMDCFilter.getPrincipal(), KMSWebApp.getKMSAudit().error(KMSMDCFilter.getUgi(),
KMSMDCFilter.getMethod(), KMSMDCFilter.getMethod(),
KMSMDCFilter.getURL(), getOneLineMessage(exception)); KMSMDCFilter.getURL(), getOneLineMessage(exception));
} }
@ -110,11 +110,11 @@ public class KMSExceptionsProvider implements ExceptionMapper<Exception> {
} }
protected void log(Response.Status status, Throwable ex) { protected void log(Response.Status status, Throwable ex) {
Principal principal = KMSMDCFilter.getPrincipal(); UserGroupInformation ugi = KMSMDCFilter.getUgi();
String method = KMSMDCFilter.getMethod(); String method = KMSMDCFilter.getMethod();
String url = KMSMDCFilter.getURL(); String url = KMSMDCFilter.getURL();
String msg = getOneLineMessage(ex); String msg = getOneLineMessage(ex);
LOG.warn("User:{} Method:{} URL:{} Response:{}-{}", principal, method, url, LOG.warn("User:'{}' Method:{} URL:{} Response:{}-{}", ugi, method, url,
status, msg, ex); status, msg, ex);
} }

View File

@ -18,6 +18,8 @@
package org.apache.hadoop.crypto.key.kms.server; package org.apache.hadoop.crypto.key.kms.server;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.delegation.web.HttpUserGroupInformation;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
@ -27,7 +29,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.security.Principal;
/** /**
* Servlet filter that captures context of the HTTP request to be use in the * Servlet filter that captures context of the HTTP request to be use in the
@ -37,12 +38,12 @@ import java.security.Principal;
public class KMSMDCFilter implements Filter { public class KMSMDCFilter implements Filter {
private static class Data { private static class Data {
private Principal principal; private UserGroupInformation ugi;
private String method; private String method;
private StringBuffer url; private StringBuffer url;
private Data(Principal principal, String method, StringBuffer url) { private Data(UserGroupInformation ugi, String method, StringBuffer url) {
this.principal = principal; this.ugi = ugi;
this.method = method; this.method = method;
this.url = url; this.url = url;
} }
@ -50,8 +51,8 @@ public class KMSMDCFilter implements Filter {
private static ThreadLocal<Data> DATA_TL = new ThreadLocal<Data>(); private static ThreadLocal<Data> DATA_TL = new ThreadLocal<Data>();
public static Principal getPrincipal() { public static UserGroupInformation getUgi() {
return DATA_TL.get().principal; return DATA_TL.get().ugi;
} }
public static String getMethod() { public static String getMethod() {
@ -72,14 +73,14 @@ public class KMSMDCFilter implements Filter {
throws IOException, ServletException { throws IOException, ServletException {
try { try {
DATA_TL.remove(); DATA_TL.remove();
Principal principal = ((HttpServletRequest) request).getUserPrincipal(); UserGroupInformation ugi = HttpUserGroupInformation.get();
String method = ((HttpServletRequest) request).getMethod(); String method = ((HttpServletRequest) request).getMethod();
StringBuffer requestURL = ((HttpServletRequest) request).getRequestURL(); StringBuffer requestURL = ((HttpServletRequest) request).getRequestURL();
String queryString = ((HttpServletRequest) request).getQueryString(); String queryString = ((HttpServletRequest) request).getQueryString();
if (queryString != null) { if (queryString != null) {
requestURL.append("?").append(queryString); requestURL.append("?").append(queryString);
} }
DATA_TL.set(new Data(principal, method, requestURL)); DATA_TL.set(new Data(ugi, method, requestURL));
chain.doFilter(request, response); chain.doFilter(request, response);
} finally { } finally {
DATA_TL.remove(); DATA_TL.remove();

View File

@ -319,6 +319,46 @@ $ keytool -genkey -alias tomcat -keyalg RSA
</configuration> </configuration>
+---+ +---+
** KMS Delegation Token Configuration
KMS delegation token secret manager can be configured with the following
properties:
+---+
<property>
<name>hadoop.kms.authentication.delegation-token.update-interval.sec</name>
<value>86400</value>
<description>
How often the master key is rotated, in seconds. Default value 1 day.
</description>
</property>
<property>
<name>hadoop.kms.authentication.delegation-token.max-lifetime.sec</name>
<value>604800</value>
<description>
Maximum lifetime of a delagation token, in seconds. Default value 7 days.
</description>
</property>
<property>
<name>hadoop.kms.authentication.delegation-token.renew-interval.sec</name>
<value>86400</value>
<description>
Renewal interval of a delagation token, in seconds. Default value 1 day.
</description>
</property>
<property>
<name>hadoop.kms.authentication.delegation-token.removal-scan-interval.sec</name>
<value>3600</value>
<description>
Scan interval to remove expired delegation tokens.
</description>
</property>
+---+
** KMS HTTP REST API ** KMS HTTP REST API
*** Create a Key *** Create a Key

View File

@ -22,8 +22,13 @@ import org.apache.hadoop.crypto.key.KeyProvider;
import org.apache.hadoop.crypto.key.KeyProvider.KeyVersion; import org.apache.hadoop.crypto.key.KeyProvider.KeyVersion;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion;
import org.apache.hadoop.crypto.key.KeyProviderDelegationTokenExtension;
import org.apache.hadoop.crypto.key.kms.KMSClientProvider; import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.minikdc.MiniKdc; import org.apache.hadoop.minikdc.MiniKdc;
import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.security.authorize.AuthorizationException;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil; import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
import org.junit.AfterClass; import org.junit.AfterClass;
@ -45,6 +50,7 @@ import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
@ -565,6 +571,17 @@ public class TestKMS {
Assert.assertEquals("d", meta.getDescription()); Assert.assertEquals("d", meta.getDescription());
Assert.assertEquals(attributes, meta.getAttributes()); Assert.assertEquals(attributes, meta.getAttributes());
KeyProviderDelegationTokenExtension kpdte =
KeyProviderDelegationTokenExtension.
createKeyProviderDelegationTokenExtension(kp);
Credentials credentials = new Credentials();
kpdte.addDelegationTokens("foo", credentials);
Assert.assertEquals(1, credentials.getAllTokens().size());
InetSocketAddress kmsAddr = new InetSocketAddress(getKMSUrl().getHost(),
getKMSUrl().getPort());
Assert.assertEquals(new Text("kms-dt"), credentials.getToken(
SecurityUtil.buildTokenService(kmsAddr)).getKind());
return null; return null;
} }
}); });
@ -596,13 +613,13 @@ public class TestKMS {
public Void call() throws Exception { public Void call() throws Exception {
final Configuration conf = new Configuration(); final Configuration conf = new Configuration();
conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 128); conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 128);
URI uri = createKMSUri(getKMSUrl()); final URI uri = createKMSUri(getKMSUrl());
final KeyProvider kp = new KMSClientProvider(uri, conf);
//nothing allowed //nothing allowed
doAs("client", new PrivilegedExceptionAction<Void>() { doAs("client", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
kp.createKey("k", new KeyProvider.Options(conf)); kp.createKey("k", new KeyProvider.Options(conf));
Assert.fail(); Assert.fail();
@ -693,6 +710,7 @@ public class TestKMS {
doAs("CREATE", new PrivilegedExceptionAction<Void>() { doAs("CREATE", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProvider.KeyVersion kv = kp.createKey("k0", KeyProvider.KeyVersion kv = kp.createKey("k0",
new KeyProvider.Options(conf)); new KeyProvider.Options(conf));
@ -707,6 +725,7 @@ public class TestKMS {
doAs("DELETE", new PrivilegedExceptionAction<Void>() { doAs("DELETE", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
kp.deleteKey("k0"); kp.deleteKey("k0");
} catch (Exception ex) { } catch (Exception ex) {
@ -719,6 +738,7 @@ public class TestKMS {
doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() { doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProvider.KeyVersion kv = kp.createKey("k1", new byte[16], KeyProvider.KeyVersion kv = kp.createKey("k1", new byte[16],
new KeyProvider.Options(conf)); new KeyProvider.Options(conf));
@ -733,6 +753,7 @@ public class TestKMS {
doAs("ROLLOVER", new PrivilegedExceptionAction<Void>() { doAs("ROLLOVER", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProvider.KeyVersion kv = kp.rollNewVersion("k1"); KeyProvider.KeyVersion kv = kp.rollNewVersion("k1");
Assert.assertNull(kv.getMaterial()); Assert.assertNull(kv.getMaterial());
@ -746,6 +767,7 @@ public class TestKMS {
doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() { doAs("SET_KEY_MATERIAL", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProvider.KeyVersion kv = KeyProvider.KeyVersion kv =
kp.rollNewVersion("k1", new byte[16]); kp.rollNewVersion("k1", new byte[16]);
@ -761,6 +783,7 @@ public class TestKMS {
doAs("GET", new PrivilegedExceptionAction<KeyVersion>() { doAs("GET", new PrivilegedExceptionAction<KeyVersion>() {
@Override @Override
public KeyVersion run() throws Exception { public KeyVersion run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
kp.getKeyVersion("k1@0"); kp.getKeyVersion("k1@0");
KeyVersion kv = kp.getCurrentKey("k1"); KeyVersion kv = kp.getCurrentKey("k1");
@ -777,6 +800,7 @@ public class TestKMS {
new PrivilegedExceptionAction<EncryptedKeyVersion>() { new PrivilegedExceptionAction<EncryptedKeyVersion>() {
@Override @Override
public EncryptedKeyVersion run() throws Exception { public EncryptedKeyVersion run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProviderCryptoExtension kpCE = KeyProviderCryptoExtension. KeyProviderCryptoExtension kpCE = KeyProviderCryptoExtension.
createKeyProviderCryptoExtension(kp); createKeyProviderCryptoExtension(kp);
@ -793,6 +817,7 @@ public class TestKMS {
doAs("DECRYPT_EEK", new PrivilegedExceptionAction<Void>() { doAs("DECRYPT_EEK", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
KeyProviderCryptoExtension kpCE = KeyProviderCryptoExtension. KeyProviderCryptoExtension kpCE = KeyProviderCryptoExtension.
createKeyProviderCryptoExtension(kp); createKeyProviderCryptoExtension(kp);
@ -807,6 +832,7 @@ public class TestKMS {
doAs("GET_KEYS", new PrivilegedExceptionAction<Void>() { doAs("GET_KEYS", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
kp.getKeys(); kp.getKeys();
} catch (Exception ex) { } catch (Exception ex) {
@ -819,6 +845,7 @@ public class TestKMS {
doAs("GET_METADATA", new PrivilegedExceptionAction<Void>() { doAs("GET_METADATA", new PrivilegedExceptionAction<Void>() {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
try { try {
kp.getMetadata("k1"); kp.getMetadata("k1");
kp.getKeysMetadata("k1"); kp.getKeysMetadata("k1");
@ -836,6 +863,7 @@ public class TestKMS {
Thread.sleep(10); // to ensure the ACLs file modifiedTime is newer Thread.sleep(10); // to ensure the ACLs file modifiedTime is newer
conf.set(KMSACLs.Type.CREATE.getConfigKey(), "foo"); conf.set(KMSACLs.Type.CREATE.getConfigKey(), "foo");
writeConf(testDir, conf); writeConf(testDir, conf);
Thread.sleep(1000);
KMSWebApp.getACLs().run(); // forcing a reload by hand. KMSWebApp.getACLs().run(); // forcing a reload by hand.
@ -844,6 +872,7 @@ public class TestKMS {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
try { try {
KeyProvider kp = new KMSClientProvider(uri, conf);
KeyProvider.KeyVersion kv = kp.createKey("k2", KeyProvider.KeyVersion kv = kp.createKey("k2",
new KeyProvider.Options(conf)); new KeyProvider.Options(conf));
Assert.fail(); Assert.fail();
@ -982,4 +1011,69 @@ public class TestKMS {
sock.close(); sock.close();
} }
@Test
public void testDelegationTokenAccess() throws Exception {
final File testDir = getTestDir();
Configuration conf = createBaseKMSConf(testDir);
conf.set("hadoop.kms.authentication.type", "kerberos");
conf.set("hadoop.kms.authentication.kerberos.keytab",
keytab.getAbsolutePath());
conf.set("hadoop.kms.authentication.kerberos.principal", "HTTP/localhost");
conf.set("hadoop.kms.authentication.kerberos.name.rules", "DEFAULT");
writeConf(testDir, conf);
runServer(null, null, testDir, new KMSCallable() {
@Override
public Void call() throws Exception {
final Configuration conf = new Configuration();
conf.setInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 64);
final URI uri = createKMSUri(getKMSUrl());
final Credentials credentials = new Credentials();
final UserGroupInformation nonKerberosUgi =
UserGroupInformation.getCurrentUser();
try {
KeyProvider kp = new KMSClientProvider(uri, conf);
kp.createKey("kA", new KeyProvider.Options(conf));
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
doAs("client", new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
KeyProviderDelegationTokenExtension kpdte =
KeyProviderDelegationTokenExtension.
createKeyProviderDelegationTokenExtension(kp);
kpdte.addDelegationTokens("foo", credentials);
return null;
}
});
nonKerberosUgi.addCredentials(credentials);
try {
KeyProvider kp = new KMSClientProvider(uri, conf);
kp.createKey("kA", new KeyProvider.Options(conf));
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
nonKerberosUgi.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
KeyProvider kp = new KMSClientProvider(uri, conf);
kp.createKey("kD", new KeyProvider.Options(conf));
return null;
}
});
return null;
}
});
}
} }

View File

@ -18,6 +18,7 @@
package org.apache.hadoop.crypto.key.kms.server; package org.apache.hadoop.crypto.key.kms.server;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -27,7 +28,8 @@ public class TestKMSACLs {
public void testDefaults() { public void testDefaults() {
KMSACLs acls = new KMSACLs(new Configuration(false)); KMSACLs acls = new KMSACLs(new Configuration(false));
for (KMSACLs.Type type : KMSACLs.Type.values()) { for (KMSACLs.Type type : KMSACLs.Type.values()) {
Assert.assertTrue(acls.hasAccess(type, "foo")); Assert.assertTrue(acls.hasAccess(type,
UserGroupInformation.createRemoteUser("foo")));
} }
} }
@ -39,8 +41,10 @@ public class TestKMSACLs {
} }
KMSACLs acls = new KMSACLs(conf); KMSACLs acls = new KMSACLs(conf);
for (KMSACLs.Type type : KMSACLs.Type.values()) { for (KMSACLs.Type type : KMSACLs.Type.values()) {
Assert.assertTrue(acls.hasAccess(type, type.toString())); Assert.assertTrue(acls.hasAccess(type,
Assert.assertFalse(acls.hasAccess(type, "foo")); UserGroupInformation.createRemoteUser(type.toString())));
Assert.assertFalse(acls.hasAccess(type,
UserGroupInformation.createRemoteUser("foo")));
} }
} }

View File

@ -21,9 +21,9 @@ import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.security.Principal;
import org.apache.hadoop.crypto.key.kms.server.KMS.KMSOp; import org.apache.hadoop.crypto.key.kms.server.KMS.KMSOp;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.log4j.LogManager; import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.PropertyConfigurator;
import org.junit.After; import org.junit.After;
@ -81,8 +81,8 @@ public class TestKMSAudit {
@Test @Test
public void testAggregation() throws Exception { public void testAggregation() throws Exception {
Principal luser = Mockito.mock(Principal.class); UserGroupInformation luser = Mockito.mock(UserGroupInformation.class);
Mockito.when(luser.getName()).thenReturn("luser"); Mockito.when(luser.getShortUserName()).thenReturn("luser");
kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg"); kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg"); kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg"); kmsAudit.ok(luser, KMSOp.DECRYPT_EEK, "k1", "testmsg");
@ -109,8 +109,8 @@ public class TestKMSAudit {
@Test @Test
public void testAggregationUnauth() throws Exception { public void testAggregationUnauth() throws Exception {
Principal luser = Mockito.mock(Principal.class); UserGroupInformation luser = Mockito.mock(UserGroupInformation.class);
Mockito.when(luser.getName()).thenReturn("luser"); Mockito.when(luser.getShortUserName()).thenReturn("luser");
kmsAudit.unauthorized(luser, KMSOp.GENERATE_EEK, "k2"); kmsAudit.unauthorized(luser, KMSOp.GENERATE_EEK, "k2");
Thread.sleep(1000); Thread.sleep(1000);
kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg"); kmsAudit.ok(luser, KMSOp.GENERATE_EEK, "k3", "testmsg");