http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONWriter.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONWriter.java b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONWriter.java new file mode 100644 index 0000000..3674e7a --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSJSONWriter.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.codehaus.jackson.map.ObjectMapper; + +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * Jersey provider that converts <code>Map</code>s and <code>List</code>s + * to their JSON representation. + */ +@Provider +@Produces(MediaType.APPLICATION_JSON) [email protected] +public class KMSJSONWriter implements MessageBodyWriter<Object> { + + @Override + public boolean isWriteable(Class<?> aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + return Map.class.isAssignableFrom(aClass) || + List.class.isAssignableFrom(aClass); + } + + @Override + public long getSize(Object obj, Class<?> aClass, Type type, + Annotation[] annotations, MediaType mediaType) { + return -1; + } + + @Override + public void writeTo(Object obj, Class<?> aClass, Type type, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap<String, Object> stringObjectMultivaluedMap, + OutputStream outputStream) throws IOException, WebApplicationException { + Writer writer = new OutputStreamWriter(outputStream); + ObjectMapper jsonMapper = new ObjectMapper(); + jsonMapper.writerWithDefaultPrettyPrinter().writeValue(writer, obj); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSMDCFilter.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSMDCFilter.java b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSMDCFilter.java new file mode 100644 index 0000000..2a3c149 --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSMDCFilter.java @@ -0,0 +1,93 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +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.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; + +/** + * Servlet filter that captures context of the HTTP request to be use in the + * scope of KMS calls on the server side. + */ [email protected] +public class KMSMDCFilter implements Filter { + + private static class Data { + private UserGroupInformation ugi; + private String method; + private StringBuffer url; + + private Data(UserGroupInformation ugi, String method, StringBuffer url) { + this.ugi = ugi; + this.method = method; + this.url = url; + } + } + + private static ThreadLocal<Data> DATA_TL = new ThreadLocal<Data>(); + + public static UserGroupInformation getUgi() { + return DATA_TL.get().ugi; + } + + public static String getMethod() { + return DATA_TL.get().method; + } + + public static String getURL() { + return DATA_TL.get().url.toString(); + } + + @Override + public void init(FilterConfig config) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) + throws IOException, ServletException { + try { + DATA_TL.remove(); + UserGroupInformation ugi = HttpUserGroupInformation.get(); + String method = ((HttpServletRequest) request).getMethod(); + StringBuffer requestURL = ((HttpServletRequest) request).getRequestURL(); + String queryString = ((HttpServletRequest) request).getQueryString(); + if (queryString != null) { + requestURL.append("?").append(queryString); + } + DATA_TL.set(new Data(ugi, method, requestURL)); + chain.doFilter(request, response); + } finally { + DATA_TL.remove(); + } + } + + @Override + public void destroy() { + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java new file mode 100644 index 0000000..24af81b --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSServerJSONUtils.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +import org.apache.commons.codec.binary.Base64; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.crypto.key.KeyProvider; +import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; +import org.apache.hadoop.crypto.key.kms.KMSRESTConstants; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * JSON utility methods for the KMS. + */ [email protected] +public class KMSServerJSONUtils { + @SuppressWarnings("unchecked") + public static Map toJSON(KeyProvider.KeyVersion keyVersion) { + Map json = new LinkedHashMap(); + if (keyVersion != null) { + json.put(KMSRESTConstants.NAME_FIELD, + keyVersion.getName()); + json.put(KMSRESTConstants.VERSION_NAME_FIELD, + keyVersion.getVersionName()); + json.put(KMSRESTConstants.MATERIAL_FIELD, + Base64.encodeBase64URLSafeString( + keyVersion.getMaterial())); + } + return json; + } + + @SuppressWarnings("unchecked") + public static List toJSON(List<KeyProvider.KeyVersion> keyVersions) { + List json = new ArrayList(); + if (keyVersions != null) { + for (KeyProvider.KeyVersion version : keyVersions) { + json.add(toJSON(version)); + } + } + return json; + } + + @SuppressWarnings("unchecked") + public static Map toJSON(EncryptedKeyVersion encryptedKeyVersion) { + Map json = new LinkedHashMap(); + if (encryptedKeyVersion != null) { + json.put(KMSRESTConstants.VERSION_NAME_FIELD, + encryptedKeyVersion.getEncryptionKeyVersionName()); + json.put(KMSRESTConstants.IV_FIELD, + Base64.encodeBase64URLSafeString( + encryptedKeyVersion.getEncryptedKeyIv())); + json.put(KMSRESTConstants.ENCRYPTED_KEY_VERSION_FIELD, + toJSON(encryptedKeyVersion.getEncryptedKeyVersion())); + } + return json; + } + + @SuppressWarnings("unchecked") + public static Map toJSON(String keyName, KeyProvider.Metadata meta) { + Map json = new LinkedHashMap(); + if (meta != null) { + json.put(KMSRESTConstants.NAME_FIELD, keyName); + json.put(KMSRESTConstants.CIPHER_FIELD, meta.getCipher()); + json.put(KMSRESTConstants.LENGTH_FIELD, meta.getBitLength()); + json.put(KMSRESTConstants.DESCRIPTION_FIELD, meta.getDescription()); + json.put(KMSRESTConstants.ATTRIBUTES_FIELD, meta.getAttributes()); + json.put(KMSRESTConstants.CREATED_FIELD, + meta.getCreated().getTime()); + json.put(KMSRESTConstants.VERSIONS_FIELD, + (long) meta.getVersions()); + } + return json; + } + + @SuppressWarnings("unchecked") + public static List toJSON(String[] keyNames, KeyProvider.Metadata[] metas) { + List json = new ArrayList(); + for (int i = 0; i < keyNames.length; i++) { + json.add(toJSON(keyNames[i], metas[i])); + } + return json; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java new file mode 100644 index 0000000..67b9f88 --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KMSWebApp.java @@ -0,0 +1,307 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.CachingKeyProvider; +import org.apache.hadoop.crypto.key.KeyProvider; +import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; +import org.apache.hadoop.crypto.key.KeyProviderFactory; +import org.apache.hadoop.crypto.key.kms.server.KeyAuthorizationKeyProvider.KeyACLs; +import org.apache.hadoop.http.HttpServer2; +import org.apache.hadoop.security.authorize.AccessControlList; +import org.apache.hadoop.util.ReflectionUtils; +import org.apache.hadoop.util.VersionInfo; +import org.apache.log4j.PropertyConfigurator; +import org.mortbay.log.Log; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; + [email protected] +public class KMSWebApp implements ServletContextListener { + + private static final String LOG4J_PROPERTIES = "kms-log4j.properties"; + + private static final String METRICS_PREFIX = "hadoop.kms."; + private static final String ADMIN_CALLS_METER = METRICS_PREFIX + + "admin.calls.meter"; + private static final String KEY_CALLS_METER = METRICS_PREFIX + + "key.calls.meter"; + private static final String INVALID_CALLS_METER = METRICS_PREFIX + + "invalid.calls.meter"; + private static final String UNAUTHORIZED_CALLS_METER = METRICS_PREFIX + + "unauthorized.calls.meter"; + private static final String UNAUTHENTICATED_CALLS_METER = METRICS_PREFIX + + "unauthenticated.calls.meter"; + private static final String GENERATE_EEK_METER = METRICS_PREFIX + + "generate_eek.calls.meter"; + private static final String DECRYPT_EEK_METER = METRICS_PREFIX + + "decrypt_eek.calls.meter"; + + private static Logger LOG; + private static MetricRegistry metricRegistry; + + private JmxReporter jmxReporter; + private static Configuration kmsConf; + private static KeyACLs kmsAcls; + private static Meter adminCallsMeter; + private static Meter keyCallsMeter; + private static Meter unauthorizedCallsMeter; + private static Meter unauthenticatedCallsMeter; + private static Meter decryptEEKCallsMeter; + private static Meter generateEEKCallsMeter; + private static Meter invalidCallsMeter; + private static KMSAudit kmsAudit; + private static KeyProviderCryptoExtension keyProviderCryptoExtension; + + static { + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + } + + private void initLogging(String confDir) { + if (System.getProperty("log4j.configuration") == null) { + System.setProperty("log4j.defaultInitOverride", "true"); + boolean fromClasspath = true; + File log4jConf = new File(confDir, LOG4J_PROPERTIES).getAbsoluteFile(); + if (log4jConf.exists()) { + PropertyConfigurator.configureAndWatch(log4jConf.getPath(), 1000); + fromClasspath = false; + } else { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL log4jUrl = cl.getResource(LOG4J_PROPERTIES); + if (log4jUrl != null) { + PropertyConfigurator.configure(log4jUrl); + } + } + LOG = LoggerFactory.getLogger(KMSWebApp.class); + LOG.debug("KMS log starting"); + if (fromClasspath) { + LOG.warn("Log4j configuration file '{}' not found", LOG4J_PROPERTIES); + LOG.warn("Logging with INFO level to standard output"); + } + } else { + LOG = LoggerFactory.getLogger(KMSWebApp.class); + } + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + try { + String confDir = System.getProperty(KMSConfiguration.KMS_CONFIG_DIR); + if (confDir == null) { + throw new RuntimeException("System property '" + + KMSConfiguration.KMS_CONFIG_DIR + "' not defined"); + } + kmsConf = KMSConfiguration.getKMSConf(); + initLogging(confDir); + LOG.info("-------------------------------------------------------------"); + LOG.info(" Java runtime version : {}", System.getProperty( + "java.runtime.version")); + LOG.info(" KMS Hadoop Version: " + VersionInfo.getVersion()); + LOG.info("-------------------------------------------------------------"); + + + kmsAcls = getAcls(kmsConf.get(KMSConfiguration.KMS_SECURITY_AUTHORIZER)); + + //kmsAcls = new KMSACLs(); + kmsAcls.startACLReloader(); + + metricRegistry = new MetricRegistry(); + jmxReporter = JmxReporter.forRegistry(metricRegistry).build(); + jmxReporter.start(); + generateEEKCallsMeter = metricRegistry.register(GENERATE_EEK_METER, + new Meter()); + decryptEEKCallsMeter = metricRegistry.register(DECRYPT_EEK_METER, + new Meter()); + adminCallsMeter = metricRegistry.register(ADMIN_CALLS_METER, new Meter()); + keyCallsMeter = metricRegistry.register(KEY_CALLS_METER, new Meter()); + invalidCallsMeter = metricRegistry.register(INVALID_CALLS_METER, + new Meter()); + unauthorizedCallsMeter = metricRegistry.register(UNAUTHORIZED_CALLS_METER, + new Meter()); + unauthenticatedCallsMeter = metricRegistry.register( + UNAUTHENTICATED_CALLS_METER, new Meter()); + + kmsAudit = + new KMSAudit(kmsConf.getLong( + KMSConfiguration.KMS_AUDIT_AGGREGATION_WINDOW, + KMSConfiguration.KMS_AUDIT_AGGREGATION_WINDOW_DEFAULT)); + + // this is required for the the JMXJsonServlet to work properly. + // the JMXJsonServlet is behind the authentication filter, + // thus the '*' ACL. + sce.getServletContext().setAttribute(HttpServer2.CONF_CONTEXT_ATTRIBUTE, + kmsConf); + sce.getServletContext().setAttribute(HttpServer2.ADMINS_ACL, + new AccessControlList(AccessControlList.WILDCARD_ACL_VALUE)); + + // intializing the KeyProvider + String providerString = kmsConf.get(KMSConfiguration.KEY_PROVIDER_URI); + if (providerString == null) { + throw new IllegalStateException("No KeyProvider has been defined"); + } + Log.info("------------------ Ranger KMSWEbApp---------------------"); + Log.info("provider string = "+providerString); + Log.info("URI = "+new URI(providerString).toString()+" scheme = "+new URI(providerString).getScheme()); + Log.info("kmsconf size= "+kmsConf.size() + " kms classname="+kmsConf.getClass().getName()); + Log.info("----------------INstantiating key provider ---------------"); + KeyProvider keyProvider = + KeyProviderFactory.get(new URI(providerString), kmsConf); + Log.info("keyProvider = "+keyProvider.toString()); + if (kmsConf.getBoolean(KMSConfiguration.KEY_CACHE_ENABLE, + KMSConfiguration.KEY_CACHE_ENABLE_DEFAULT)) { + long keyTimeOutMillis = + kmsConf.getLong(KMSConfiguration.KEY_CACHE_TIMEOUT_KEY, + KMSConfiguration.KEY_CACHE_TIMEOUT_DEFAULT); + long currKeyTimeOutMillis = + kmsConf.getLong(KMSConfiguration.CURR_KEY_CACHE_TIMEOUT_KEY, + KMSConfiguration.CURR_KEY_CACHE_TIMEOUT_DEFAULT); + keyProvider = new CachingKeyProvider(keyProvider, keyTimeOutMillis, + currKeyTimeOutMillis); + } + LOG.info("Initialized KeyProvider " + keyProvider); + + keyProviderCryptoExtension = KeyProviderCryptoExtension. + createKeyProviderCryptoExtension(keyProvider); + keyProviderCryptoExtension = + new EagerKeyGeneratorKeyProviderCryptoExtension(kmsConf, + keyProviderCryptoExtension); + if (kmsConf.getBoolean(KMSConfiguration.KEY_AUTHORIZATION_ENABLE, + KMSConfiguration.KEY_AUTHORIZATION_ENABLE_DEFAULT)) { + keyProviderCryptoExtension = + new KeyAuthorizationKeyProvider( + keyProviderCryptoExtension, kmsAcls); + } + + LOG.info("Initialized KeyProviderCryptoExtension " + + keyProviderCryptoExtension); + final int defaultBitlength = kmsConf + .getInt(KeyProvider.DEFAULT_BITLENGTH_NAME, + KeyProvider.DEFAULT_BITLENGTH); + LOG.info("Default key bitlength is {}", defaultBitlength); + LOG.info("Ranger KMS Started"); + } catch (Throwable ex) { + System.out.println(); + System.out.println("ERROR: Hadoop KMS could not be started"); + System.out.println(); + System.out.println("REASON: " + ex.toString()); + System.out.println(); + System.out.println("Stacktrace:"); + System.out.println("---------------------------------------------------"); + ex.printStackTrace(System.out); + System.out.println("---------------------------------------------------"); + System.out.println(); + System.exit(1); + } + } + + private KeyACLs getAcls(String clsStr) throws IOException { + KeyACLs keyAcl = null; + try { + Class<? extends KeyACLs> cls = null; + if (clsStr == null || clsStr.trim().equals("")) { + cls = KMSACLs.class; + } else { + //Class<?> configClass = Class.forName(clsStr, true, JavaUtils.getClassLoader()); + Class<?> configClass = Class.forName(clsStr); + //Class<?> configClass = Class.forName(clsStr, true, JavaUtils.getClassLoader()); + if(!KeyACLs.class.isAssignableFrom(configClass) ){ + //if it's not of type KeyACLs + //we can have default also "cls = KMSACLs.class;" + return null; + } + cls = (Class<? extends KeyACLs>)configClass; + } + if (cls != null) { + keyAcl = ReflectionUtils.newInstance(cls, kmsConf); + } + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + return keyAcl; + } + +@Override + public void contextDestroyed(ServletContextEvent sce) { + kmsAudit.shutdown(); + kmsAcls.stopACLReloader(); + jmxReporter.stop(); + jmxReporter.close(); + metricRegistry = null; + LOG.info("KMS Stopped"); + } + + public static Configuration getConfiguration() { + return new Configuration(kmsConf); + } + + public static KeyACLs getACLs() { + return kmsAcls; + } + + public static Meter getAdminCallsMeter() { + return adminCallsMeter; + } + + public static Meter getKeyCallsMeter() { + return keyCallsMeter; + } + + public static Meter getInvalidCallsMeter() { + return invalidCallsMeter; + } + + public static Meter getGenerateEEKCallsMeter() { + return generateEEKCallsMeter; + } + + public static Meter getDecryptEEKCallsMeter() { + return decryptEEKCallsMeter; + } + + public static Meter getUnauthorizedCallsMeter() { + return unauthorizedCallsMeter; + } + + public static Meter getUnauthenticatedCallsMeter() { + return unauthenticatedCallsMeter; + } + + public static KeyProviderCryptoExtension getKeyProvider() { + return keyProviderCryptoExtension; + } + + public static KMSAudit getKMSAudit() { + return kmsAudit; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java new file mode 100644 index 0000000..5099daf --- /dev/null +++ b/kms/src/main/java/org/apache/hadoop/crypto/key/kms/server/KeyAuthorizationKeyProvider.java @@ -0,0 +1,299 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.crypto.key.kms.server; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; + +import org.apache.hadoop.crypto.key.KeyProvider; +import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; +import org.apache.hadoop.crypto.key.kms.server.KMS.KMSOp; +import org.apache.hadoop.security.AccessControlException; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AuthorizationException; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; + +/** + * A {@link KeyProvider} proxy that checks whether the current user derived via + * {@link UserGroupInformation}, is authorized to perform the following + * type of operations on a Key : + * <ol> + * <li>MANAGEMENT operations : createKey, rollNewVersion, deleteKey</li> + * <li>GENERATE_EEK operations : generateEncryptedKey, warmUpEncryptedKeys</li> + * <li>DECRYPT_EEK operation : decryptEncryptedKey</li> + * <li>READ operations : getKeyVersion, getKeyVersions, getMetadata, + * getKeysMetadata, getCurrentKey</li> + * </ol> + * The read operations (getCurrentKeyVersion / getMetadata) etc are not checked. + */ +public class KeyAuthorizationKeyProvider extends KeyProviderCryptoExtension { + + public static final String KEY_ACL = "key.acl."; + private static final String KEY_ACL_NAME = KEY_ACL + "name"; + + public enum KeyOpType { + ALL, READ, MANAGEMENT, GENERATE_EEK, DECRYPT_EEK; + } + + /** + * Interface that needs to be implemented by a client of the + * <code>KeyAuthorizationKeyProvider</code>. + */ + public static interface KeyACLs { + + /** + * This is called by the KeyProvider to check if the given user is + * authorized to perform the specified operation on the given acl name. + * @param aclName name of the key ACL + * @param ugi User's UserGroupInformation + * @param opType Operation Type + * @return true if user has access to the aclName and opType else false + */ + public boolean hasAccessToKey(String aclName, UserGroupInformation ugi, + KeyOpType opType); + + /** + * + * @param aclName ACL name + * @param opType Operation Type + * @return true if AclName exists else false + */ + public boolean isACLPresent(String aclName, KeyOpType opType); + + public void startACLReloader(); + + public void stopACLReloader(); + + public boolean hasAccess(KMSACLsType.Type aclType, UserGroupInformation ugi); + + public void assertAccess(KMSACLsType.Type aclType, UserGroupInformation ugi, + KMSOp operation, String key) throws AccessControlException; + } + + private final KeyProviderCryptoExtension provider; + private final KeyACLs acls; + + /** + * The constructor takes a {@link KeyProviderCryptoExtension} and an + * implementation of <code>KeyACLs</code>. All calls are delegated to the + * provider keyProvider after authorization check (if required) + * @param keyProvider + * @param acls + */ + public KeyAuthorizationKeyProvider(KeyProviderCryptoExtension keyProvider, + KeyACLs acls) { + super(keyProvider, null); + this.provider = keyProvider; + this.acls = acls; + } + + // This method first checks if "key.acl.name" attribute is present as an + // attribute in the provider Options. If yes, use the aclName for any + // subsequent access checks, else use the keyName as the aclName and set it + // as the value of the "key.acl.name" in the key's metadata. + private void authorizeCreateKey(String keyName, Options options, + UserGroupInformation ugi) throws IOException{ + Preconditions.checkNotNull(ugi, "UserGroupInformation cannot be null"); + Map<String, String> attributes = options.getAttributes(); + String aclName = attributes.get(KEY_ACL_NAME); + boolean success = false; + if (Strings.isNullOrEmpty(aclName)) { + if (acls.isACLPresent(keyName, KeyOpType.MANAGEMENT)) { + options.setAttributes(ImmutableMap.<String, String> builder() + .putAll(attributes).put(KEY_ACL_NAME, keyName).build()); + success = + acls.hasAccessToKey(keyName, ugi, KeyOpType.MANAGEMENT) + || acls.hasAccessToKey(keyName, ugi, KeyOpType.ALL); + } else { + success = false; + } + } else { + success = acls.isACLPresent(aclName, KeyOpType.MANAGEMENT) && + (acls.hasAccessToKey(aclName, ugi, KeyOpType.MANAGEMENT) + || acls.hasAccessToKey(aclName, ugi, KeyOpType.ALL)); + } + if (!success) + throw new AuthorizationException(String.format("User [%s] is not" + + " authorized to create key !!", ugi.getShortUserName())); + } + + private void checkAccess(String aclName, UserGroupInformation ugi, + KeyOpType opType) throws AuthorizationException { + Preconditions.checkNotNull(aclName, "Key ACL name cannot be null"); + Preconditions.checkNotNull(ugi, "UserGroupInformation cannot be null"); + if (acls.isACLPresent(aclName, KeyOpType.MANAGEMENT) && + (acls.hasAccessToKey(aclName, ugi, opType) + || acls.hasAccessToKey(aclName, ugi, KeyOpType.ALL))) { + return; + } else { + throw new AuthorizationException(String.format("User [%s] is not" + + " authorized to perform [%s] on key with ACL name [%s]!!", + ugi.getShortUserName(), opType, aclName)); + } + } + + @Override + public KeyVersion createKey(String name, Options options) + throws NoSuchAlgorithmException, IOException { + authorizeCreateKey(name, options, getUser()); + return provider.createKey(name, options); + } + + @Override + public KeyVersion createKey(String name, byte[] material, Options options) + throws IOException { + authorizeCreateKey(name, options, getUser()); + return provider.createKey(name, material, options); + } + + @Override + public KeyVersion rollNewVersion(String name) + throws NoSuchAlgorithmException, IOException { + doAccessCheck(name, KeyOpType.MANAGEMENT); + return provider.rollNewVersion(name); + } + + @Override + public void deleteKey(String name) throws IOException { + doAccessCheck(name, KeyOpType.MANAGEMENT); + provider.deleteKey(name); + } + + @Override + public KeyVersion rollNewVersion(String name, byte[] material) + throws IOException { + doAccessCheck(name, KeyOpType.MANAGEMENT); + return provider.rollNewVersion(name, material); + } + + @Override + public void warmUpEncryptedKeys(String... names) throws IOException { + for (String name : names) { + doAccessCheck(name, KeyOpType.GENERATE_EEK); + } + provider.warmUpEncryptedKeys(names); + } + + @Override + public EncryptedKeyVersion generateEncryptedKey(String encryptionKeyName) + throws IOException, GeneralSecurityException { + doAccessCheck(encryptionKeyName, KeyOpType.GENERATE_EEK); + return provider.generateEncryptedKey(encryptionKeyName); + } + + private void verifyKeyVersionBelongsToKey(EncryptedKeyVersion ekv) + throws IOException { + String kn = ekv.getEncryptionKeyName(); + String kvn = ekv.getEncryptionKeyVersionName(); + KeyVersion kv = provider.getKeyVersion(kvn); + if (!kv.getName().equals(kn)) { + throw new IllegalArgumentException(String.format( + "KeyVersion '%s' does not belong to the key '%s'", kvn, kn)); + } + } + + @Override + public KeyVersion decryptEncryptedKey(EncryptedKeyVersion encryptedKeyVersion) + throws IOException, GeneralSecurityException { + verifyKeyVersionBelongsToKey(encryptedKeyVersion); + doAccessCheck( + encryptedKeyVersion.getEncryptionKeyName(), KeyOpType.DECRYPT_EEK); + return provider.decryptEncryptedKey(encryptedKeyVersion); + } + + @Override + public KeyVersion getKeyVersion(String versionName) throws IOException { + KeyVersion keyVersion = provider.getKeyVersion(versionName); + if (keyVersion != null) { + doAccessCheck(keyVersion.getName(), KeyOpType.READ); + } + return keyVersion; + } + + @Override + public List<String> getKeys() throws IOException { + return provider.getKeys(); + } + + @Override + public List<KeyVersion> getKeyVersions(String name) throws IOException { + doAccessCheck(name, KeyOpType.READ); + return provider.getKeyVersions(name); + } + + @Override + public Metadata getMetadata(String name) throws IOException { + doAccessCheck(name, KeyOpType.READ); + return provider.getMetadata(name); + } + + @Override + public Metadata[] getKeysMetadata(String... names) throws IOException { + for (String name : names) { + doAccessCheck(name, KeyOpType.READ); + } + return provider.getKeysMetadata(names); + } + + @Override + public KeyVersion getCurrentKey(String name) throws IOException { + doAccessCheck(name, KeyOpType.READ); + return provider.getCurrentKey(name); + } + + @Override + public void flush() throws IOException { + provider.flush(); + } + + @Override + public boolean isTransient() { + return provider.isTransient(); + } + + private void doAccessCheck(String keyName, KeyOpType opType) throws + IOException { + Metadata metadata = provider.getMetadata(keyName); + if (metadata != null) { + String aclName = metadata.getAttributes().get(KEY_ACL_NAME); + checkAccess((aclName == null) ? keyName : aclName, getUser(), opType); + } + } + + private UserGroupInformation getUser() throws IOException { + return UserGroupInformation.getCurrentUser(); + } + + @Override + protected KeyProvider getKeyProvider() { + return this; + } + + @Override + public String toString() { + return provider.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/entity/XXDBBase.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/entity/XXDBBase.java b/kms/src/main/java/org/apache/ranger/entity/XXDBBase.java new file mode 100644 index 0000000..08ab1f7 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/entity/XXDBBase.java @@ -0,0 +1,216 @@ +package org.apache.ranger.entity; + +/** + * Base JPA class with id, versionNumber and other common attributes + * + */ + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import javax.persistence.Column; +import javax.persistence.MappedSuperclass; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.xml.bind.annotation.XmlRootElement; + +@MappedSuperclass +@XmlRootElement +public abstract class XXDBBase extends Object implements java.io.Serializable { + private static final long serialVersionUID = 1L; + public static final int CLASS_TYPE_NONE = 0; + private static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT+0"); + + /** + * Date/Time creation of this user. + * <ul> + * </ul> + * + */ + @Temporal(TemporalType.TIMESTAMP) + @Column(name="CREATE_TIME" ) + protected Date createTime = getUTCDate(); + + /** + * Date value. + * <ul> + * </ul> + * + */ + @Temporal(TemporalType.TIMESTAMP) + @Column(name="UPDATE_TIME" ) + protected Date updateTime = getUTCDate(); + + /** + * Added by + * <ul> + * </ul> + * + */ + @Column(name="ADDED_BY_ID" ) + protected Long addedByUserId; + + + /** + * Last updated by + * <ul> + * </ul> + * + */ + @Column(name="UPD_BY_ID" ) + protected Long updatedByUserId; + + + /** + * Default constructor. This will set all the attributes to default value. + */ + public XXDBBase ( ) { + } + + public int getMyClassType( ) { + return CLASS_TYPE_NONE; + } + + public String getMyDisplayValue() { + return null; + } + + /** + * This method sets the value to the member attribute <b>id</b>. + * You cannot set null to the attribute. + * @param id Value to set member attribute <b>id</b> + */ + public abstract void setId( Long id ) ; + + /** + * Returns the value for the member attribute <b>id</b> + * @return Long - value of member attribute <b>id</b>. + */ + public abstract Long getId( ); + + /** + * This method sets the value to the member attribute <b>createTime</b>. + * You cannot set null to the attribute. + * @param createTime Value to set member attribute <b>createTime</b> + */ + public void setCreateTime( Date createTime ) { + this.createTime = createTime; + } + + /** + * Returns the value for the member attribute <b>createTime</b> + * @return Date - value of member attribute <b>createTime</b>. + */ + public Date getCreateTime( ) { + return this.createTime; + } + + /** + * This method sets the value to the member attribute <b>updateTime</b>. + * You cannot set null to the attribute. + * @param updateTime Value to set member attribute <b>updateTime</b> + */ + public void setUpdateTime( Date updateTime ) { + this.updateTime = updateTime; + } + + /** + * Returns the value for the member attribute <b>updateTime</b> + * @return Date - value of member attribute <b>updateTime</b>. + */ + public Date getUpdateTime( ) { + return this.updateTime; + } + + /** + * This method sets the value to the member attribute <b>addedByUserId</b>. + * You cannot set null to the attribute. + * @param addedByUserId Value to set member attribute <b>addedByUserId</b> + */ + public void setAddedByUserId( Long addedByUserId ) { + this.addedByUserId = addedByUserId; + } + + /** + * Returns the value for the member attribute <b>addedByUserId</b> + * @return Long - value of member attribute <b>addedByUserId</b>. + */ + public Long getAddedByUserId( ) { + return this.addedByUserId; + } + + + /** + * This method sets the value to the member attribute <b>updatedByUserId</b>. + * You cannot set null to the attribute. + * @param updatedByUserId Value to set member attribute <b>updatedByUserId</b> + */ + public void setUpdatedByUserId( Long updatedByUserId ) { + this.updatedByUserId = updatedByUserId; + } + + /** + * Returns the value for the member attribute <b>updatedByUserId</b> + * @return Long - value of member attribute <b>updatedByUserId</b>. + */ + public Long getUpdatedByUserId( ) { + return this.updatedByUserId; + } + + + /** + * This return the bean content in string format + * @return formatedStr + */ + @Override + public String toString( ) { + String str = "XXDBBase={"; + //`str += "id={" + id + "} "; + str += "createTime={" + createTime + "} "; + str += "updateTime={" + updateTime + "} "; + str += "addedByUserId={" + addedByUserId + "} "; + str += "updatedByUserId={" + updatedByUserId + "} "; + str += "}"; + return str; + } + + /** + * Checks for all attributes except referenced db objects + * @return true if all attributes match + */ + @Override + public boolean equals( Object obj) { + XXDBBase other = (XXDBBase) obj; + if ((this.createTime == null && other.createTime != null) || (this.createTime != null && !this.createTime.equals(other.createTime))) { + return false; + } + if ((this.updateTime == null && other.updateTime != null) || (this.updateTime != null && !this.updateTime.equals(other.updateTime))) { + return false; + } + if ((this.addedByUserId == null && other.addedByUserId != null) || (this.addedByUserId != null && !this.addedByUserId.equals(other.addedByUserId))) { + return false; + } + if ((this.updatedByUserId == null && other.updatedByUserId != null) || (this.updatedByUserId != null && !this.updatedByUserId.equals(other.updatedByUserId))) { + return false; + } + return true; + } + public static String getEnumName(String fieldName ) { + return null; + } + + private static Date getUTCDate(){ + try{ + Calendar local=Calendar.getInstance(); + int offset = local.getTimeZone().getOffset(local.getTimeInMillis()); + GregorianCalendar utc = new GregorianCalendar(gmtTimeZone); + utc.setTimeInMillis(local.getTimeInMillis()); + utc.add(Calendar.MILLISECOND, -offset); + return utc.getTime(); + }catch(Exception ex){ + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/entity/XXRangerKeyStore.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/entity/XXRangerKeyStore.java b/kms/src/main/java/org/apache/ranger/entity/XXRangerKeyStore.java new file mode 100644 index 0000000..c9eb77e --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/entity/XXRangerKeyStore.java @@ -0,0 +1,121 @@ +package org.apache.ranger.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlRootElement; + +@Entity +@Table(name="ranger_keystore") +@XmlRootElement +public class XXRangerKeyStore extends XXDBBase implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + @Id + @SequenceGenerator(name="kmskeys",sequenceName="kmskeys",allocationSize=1) + @GeneratedValue(strategy=GenerationType.AUTO,generator="kmskeys") + @Column(name="ID") + protected Long id; + @Override + public void setId(Long id) { + this.id=id; + } + + @Override + public Long getId() { + return id; + } + + @Column(name="kms_alias" , length=255 ) + protected String alias; + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + @Column(name="kms_createdDate" , length=255 ) + protected Long createdDate; + + public Long getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Long createdDate) { + this.createdDate = createdDate; + } + + @Lob + @Column(name="kms_encoded") + protected String encoded; + + public String getEncoded() { + return encoded; + } + + public void setEncoded(String encoded) { + this.encoded = encoded; + } + + @Column(name="kms_cipher" , length=255) + protected String cipher; + + public String getCipher() { + return cipher; + } + + public void setCipher(String cipher) { + this.cipher = cipher; + } + + @Column(name="kms_bitLength") + protected int bitLength; + + public int getBitLength() { + return bitLength; + } + + public void setBitLength(int bitLength) { + this.bitLength = bitLength; + } + + @Column(name="kms_description") + protected String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Column(name="kms_version") + protected int version; + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + @Column(name="kms_attributes") + protected String attributes; + public String getAttributes() { + return attributes; + } + + public void setAttributes(String attributes) { + this.attributes = attributes; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/entity/XXRangerMasterKey.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/entity/XXRangerMasterKey.java b/kms/src/main/java/org/apache/ranger/entity/XXRangerMasterKey.java new file mode 100644 index 0000000..6175286 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/entity/XXRangerMasterKey.java @@ -0,0 +1,67 @@ +package org.apache.ranger.entity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.xml.bind.annotation.XmlRootElement; + +@Entity +@Table(name="ranger_masterkey") +@XmlRootElement +public class XXRangerMasterKey extends XXDBBase implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + @Id + @SequenceGenerator(name="rangermasterkey",sequenceName="rangermasterkey",allocationSize=1) + @GeneratedValue(strategy=GenerationType.AUTO,generator="rangermasterkey") + @Column(name="ID") + protected Long id; + @Override + public void setId(Long id) { + this.id=id; + } + + @Override + public Long getId() { + return id; + } + + @Column(name="cipher") + protected String cipher; + + public String getCipher() { + return cipher; + } + + public void setCipher(String cipher) { + this.cipher = cipher; + } + + @Column(name="bitlength") + protected int bitLength; + + public int getBitLength() { + return bitLength; + } + + public void setBitLength(int bitLength) { + this.bitLength = bitLength; + } + + @Lob + @Column(name="masterkey") + protected String masterKey; + + public String getMasterKey() { + return masterKey; + } + + public void setMasterKey(String masterKey) { + this.masterKey = masterKey; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/biz/RangerKMSStartUp.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/biz/RangerKMSStartUp.java b/kms/src/main/java/org/apache/ranger/kms/biz/RangerKMSStartUp.java new file mode 100644 index 0000000..af9bfa1 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/biz/RangerKMSStartUp.java @@ -0,0 +1,35 @@ +package org.apache.ranger.kms.biz; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServlet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.RangerKeyStoreProvider; +import org.apache.hadoop.crypto.key.RangerMasterKey; +import org.springframework.stereotype.Component; + +@SuppressWarnings("serial") +@Component +public class RangerKMSStartUp extends HttpServlet +{ + public static final String ENCRYPTION_KEY = "ranger.db.encrypt.key.password"; + private static Logger LOG = LoggerFactory.getLogger(RangerKMSStartUp.class); + + @PostConstruct + public void initRangerMasterKey() { + LOG.info("Ranger KMSStartUp"); + RangerMasterKey rangerMasterKey = new RangerMasterKey(); + try { + Configuration conf = RangerKeyStoreProvider.getDBKSConf(); + String password = conf.get(ENCRYPTION_KEY); + boolean check = rangerMasterKey.generateMasterKey(password); + if(check){ + LOG.info("MasterKey Generated.."); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/dao/BaseDao.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/dao/BaseDao.java b/kms/src/main/java/org/apache/ranger/kms/dao/BaseDao.java new file mode 100644 index 0000000..f835bcc --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/dao/BaseDao.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + package org.apache.ranger.kms.dao; + + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; +import javax.persistence.NoResultException; +import javax.persistence.TypedQuery; + +import org.apache.log4j.Logger; + +public abstract class BaseDao<T> { + static final Logger logger = Logger.getLogger(BaseDao.class); + + protected DaoManager daoManager; + + protected Class<T> tClass; + + public BaseDao(DaoManagerBase daoManager) { + this.init(daoManager); + } + + @SuppressWarnings("unchecked") + private void init(DaoManagerBase daoManager) { + this.daoManager = (DaoManager) daoManager; + + ParameterizedType genericSuperclass = (ParameterizedType) getClass() + .getGenericSuperclass(); + + Type type = genericSuperclass.getActualTypeArguments()[0]; + + if (type instanceof ParameterizedType) { + this.tClass = (Class<T>) ((ParameterizedType) type).getRawType(); + } else { + this.tClass = (Class<T>) type; + } + } + + public EntityManager getEntityManager() { + return daoManager.getEntityManager(); + } + + public boolean beginTransaction() { + boolean ret = false; + + EntityManager em = getEntityManager(); + + if(em != null) { + EntityTransaction et = em.getTransaction(); + + // check the transaction is not already active + if(et != null && !et.isActive()) { + et.begin(); + ret = true; + } + } + + return ret; + } + + public void commitTransaction() { + EntityManager em = getEntityManager(); + + if(em != null) { + em.flush(); + + EntityTransaction et = em.getTransaction(); + + if(et != null) { + et.commit(); + } + } + } + + public void rollbackTransaction() { + EntityManager em = getEntityManager(); + + if(em != null) { + EntityTransaction et = em.getTransaction(); + + if(et != null) { + et.rollback(); + } + } + } + + public T create(T obj) { + T ret = null; + + boolean trxBegan = beginTransaction(); + + getEntityManager().persist(obj); + + if(trxBegan) { + commitTransaction(); + } + + ret = obj; + + return ret; + } + + public T update(T obj) { + boolean trxBegan = beginTransaction(); + + getEntityManager().merge(obj); + + if(trxBegan) { + commitTransaction(); + } + + return obj; + } + + public boolean remove(Long id) { + return remove(getById(id)); + } + + public boolean remove(T obj) { + if (obj == null) { + return true; + } + + boolean ret = false; + + boolean trxBegan = beginTransaction(); + + getEntityManager().remove(obj); + + if(trxBegan) { + commitTransaction(); + } + + ret = true; + + return ret; + } + + public T getById(Long id) { + if (id == null) { + return null; + } + T ret = null; + try { + ret = getEntityManager().find(tClass, id); + } catch (NoResultException e) { + return null; + } + return ret; + } + + public List<T> getAll() { + List<T> ret = null; + + TypedQuery<T> qry = getEntityManager().createQuery( + "SELECT t FROM " + tClass.getSimpleName() + " t", tClass); + + ret = qry.getResultList(); + + return ret; + } + + public Long getAllCount() { + Long ret = null; + + TypedQuery<Long> qry = getEntityManager().createQuery( + "SELECT count(t) FROM " + tClass.getSimpleName() + " t", + Long.class); + + ret = qry.getSingleResult(); + + return ret; + } + + public T getUniqueResult(TypedQuery<T> qry) { + T ret = null; + + try { + ret = qry.getSingleResult(); + } catch (NoResultException e) { + // ignore + } + return ret; + } + + public List<T> executeQuery(TypedQuery<T> qry) { + List<T> ret = null; + + ret = qry.getResultList(); + + return ret; + } + + public List<T> findByNamedQuery(String namedQuery, String paramName, + Object refId) { + List<T> ret = new ArrayList<T>(); + + if (namedQuery == null) { + return ret; + } + try { + TypedQuery<T> qry = getEntityManager().createNamedQuery(namedQuery, tClass); + qry.setParameter(paramName, refId); + ret = qry.getResultList(); + } catch (NoResultException e) { + // ignore + } + return ret; + } + + public T findByAlias(String namedQuery, String alias) { + try { + return getEntityManager() + .createNamedQuery(namedQuery, tClass) + .setParameter("alias", alias) + .getSingleResult(); + } catch (NoResultException e) { + } + return null; + } + + public int deleteByAlias(String namedQuery, String alias) { + boolean trxBegan = beginTransaction(); + try { + int i = getEntityManager() + .createNamedQuery(namedQuery, tClass) + .setParameter("alias", alias).executeUpdate(); + if(trxBegan) { + commitTransaction(); + } + return i; + } catch (NoResultException e) { + e.printStackTrace(); + rollbackTransaction(); + } + return 0; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/dao/DaoManager.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/dao/DaoManager.java b/kms/src/main/java/org/apache/ranger/kms/dao/DaoManager.java new file mode 100644 index 0000000..be67204 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/dao/DaoManager.java @@ -0,0 +1,35 @@ +package org.apache.ranger.kms.dao; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceContext; + +public class DaoManager extends DaoManagerBase { + + @PersistenceContext + private EntityManagerFactory emf; + + static ThreadLocal<EntityManager> sEntityManager; + + public void setEntityManagerFactory(EntityManagerFactory emf) { + this.emf = emf; + sEntityManager = new ThreadLocal<EntityManager>(); + } + + @Override + public EntityManager getEntityManager() { + EntityManager em = null; + + if(sEntityManager != null) { + em = sEntityManager.get(); + + if(em == null && this.emf != null) { + em = this.emf.createEntityManager(); + + sEntityManager.set(em); + } + } + + return em; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/dao/DaoManagerBase.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/dao/DaoManagerBase.java b/kms/src/main/java/org/apache/ranger/kms/dao/DaoManagerBase.java new file mode 100644 index 0000000..5cd84b0 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/dao/DaoManagerBase.java @@ -0,0 +1,31 @@ +package org.apache.ranger.kms.dao; + +import javax.persistence.EntityManager; +import org.apache.log4j.Logger; + +public abstract class DaoManagerBase { + final static Logger logger = Logger.getLogger(DaoManagerBase.class); + + abstract public EntityManager getEntityManager(); + + private RangerMasterKeyDao rangerMasterKeyDao = null; + private RangerKMSDao rangerKmsDao = null; + + public DaoManagerBase() { + } + + public RangerMasterKeyDao getRangerMasterKeyDao() { + if(rangerMasterKeyDao == null) { + rangerMasterKeyDao = new RangerMasterKeyDao(this); + } + + return rangerMasterKeyDao; + } + + public RangerKMSDao getRangerKMSDao(){ + if(rangerKmsDao == null){ + rangerKmsDao = new RangerKMSDao(this); + } + return rangerKmsDao; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/dao/RangerKMSDao.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/dao/RangerKMSDao.java b/kms/src/main/java/org/apache/ranger/kms/dao/RangerKMSDao.java new file mode 100644 index 0000000..d5c94fe --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/dao/RangerKMSDao.java @@ -0,0 +1,18 @@ +package org.apache.ranger.kms.dao; + +import org.apache.ranger.entity.XXRangerKeyStore; + +public class RangerKMSDao extends BaseDao<XXRangerKeyStore> { + + public RangerKMSDao(DaoManagerBase daoManager) { + super(daoManager); + } + + public XXRangerKeyStore findByAlias(String alias){ + return super.findByAlias("XXRangerKeyStore.findByAlias", alias); + } + + public int deleteByAlias(String alias){ + return super.deleteByAlias("XXRangerKeyStore.deleteByAlias", alias); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/java/org/apache/ranger/kms/dao/RangerMasterKeyDao.java ---------------------------------------------------------------------- diff --git a/kms/src/main/java/org/apache/ranger/kms/dao/RangerMasterKeyDao.java b/kms/src/main/java/org/apache/ranger/kms/dao/RangerMasterKeyDao.java new file mode 100644 index 0000000..4170433 --- /dev/null +++ b/kms/src/main/java/org/apache/ranger/kms/dao/RangerMasterKeyDao.java @@ -0,0 +1,10 @@ +package org.apache.ranger.kms.dao; + +import org.apache.ranger.entity.XXRangerMasterKey; + +public class RangerMasterKeyDao extends BaseDao<XXRangerMasterKey> { + + public RangerMasterKeyDao(DaoManagerBase daoManager) { + super(daoManager); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/META-INF/kms_jpa_named_queries.xml ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/META-INF/kms_jpa_named_queries.xml b/kms/src/main/resources/META-INF/kms_jpa_named_queries.xml new file mode 100644 index 0000000..8fd3128 --- /dev/null +++ b/kms/src/main/resources/META-INF/kms_jpa_named_queries.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<entity-mappings version="1.0" + xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd "> + <named-query name="XXRangerKeyStore.findByAlias"> + <query>SELECT Obj FROM XXRangerKeyStore obj + WHERE obj.alias=:alias + </query> + </named-query> + + <named-query name="XXRangerKeyStore.deleteByAlias"> + <query>DELETE FROM XXRangerKeyStore obj + WHERE obj.alias=:alias + </query> + </named-query> +</entity-mappings> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/META-INF/persistence.xml ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/META-INF/persistence.xml b/kms/src/main/resources/META-INF/persistence.xml new file mode 100644 index 0000000..31c0bc4 --- /dev/null +++ b/kms/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> + <persistence-unit name="persistence_ranger_server"> + <mapping-file>META-INF/kms_jpa_named_queries.xml</mapping-file> + <class>org.apache.ranger.entity.XXRangerMasterKey</class> + <class>org.apache.ranger.entity.XXRangerKeyStore</class> + + <properties> + <property name="eclipselink.logging.level" value="SEVERE"/> + </properties> + </persistence-unit> +</persistence> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/META-INF/services/org.apache.hadoop.crypto.key.KeyProviderFactory ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/META-INF/services/org.apache.hadoop.crypto.key.KeyProviderFactory b/kms/src/main/resources/META-INF/services/org.apache.hadoop.crypto.key.KeyProviderFactory new file mode 100644 index 0000000..bb9f0bb --- /dev/null +++ b/kms/src/main/resources/META-INF/services/org.apache.hadoop.crypto.key.KeyProviderFactory @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +org.apache.hadoop.crypto.key.RangerKeyStoreProvider$Factory +org.apache.hadoop.crypto.key.JavaKeyStoreProvider$Factory +org.apache.hadoop.crypto.key.UserProvider$Factory +org.apache.hadoop.crypto.key.kms.KMSClientProvider$Factory http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/WEB-INF/web.xml b/kms/src/main/resources/WEB-INF/web.xml new file mode 100644 index 0000000..71c580c --- /dev/null +++ b/kms/src/main/resources/WEB-INF/web.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"> + + <display-name>ranger-kms</display-name> + + <listener> + <listener-class>org.apache.hadoop.crypto.key.kms.server.KMSWebApp</listener-class> + </listener> + + <servlet> + <servlet-name>webservices-driver</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.packages</param-name> + <param-value>org.apache.hadoop.crypto.key.kms.server</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + +<!-- <servlet> + <servlet-name>RangerKMSStartUp</servlet-name> + <servlet-class>org.apache.ranger.kms.biz.RangerKMSStartUp</servlet-class> + <load-on-startup>2</load-on-startup> + </servlet> --> + + <servlet> + <servlet-name>jmx-servlet</servlet-name> + <servlet-class>org.apache.hadoop.crypto.key.kms.server.KMSJMXServlet</servlet-class> + </servlet> + + <servlet-mapping> + <servlet-name>webservices-driver</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>jmx-servlet</servlet-name> + <url-pattern>/jmx</url-pattern> + </servlet-mapping> + + <filter> + <filter-name>authFilter</filter-name> + <filter-class>org.apache.hadoop.crypto.key.kms.server.KMSAuthenticationFilter</filter-class> + </filter> + + <filter> + <filter-name>MDCFilter</filter-name> + <filter-class>org.apache.hadoop.crypto.key.kms.server.KMSMDCFilter</filter-class> + </filter> + + <filter-mapping> + <filter-name>authFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <filter-mapping> + <filter-name>MDCFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + +</web-app> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/log4j-kmsaudit.properties ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/log4j-kmsaudit.properties b/kms/src/main/resources/log4j-kmsaudit.properties new file mode 100644 index 0000000..cca6941 --- /dev/null +++ b/kms/src/main/resources/log4j-kmsaudit.properties @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# LOG Appender +log4j.appender.kms-audit=org.apache.log4j.ConsoleAppender +log4j.appender.kms-audit.Target=System.err +log4j.appender.kms-audit.layout=org.apache.log4j.PatternLayout +log4j.appender.kms-audit.layout.ConversionPattern=%m + +log4j.rootLogger=INFO, kms-audit \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/log4j.properties b/kms/src/main/resources/log4j.properties new file mode 100644 index 0000000..5cd037a --- /dev/null +++ b/kms/src/main/resources/log4j.properties @@ -0,0 +1,31 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# STDOUT Appender +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{1} - %m%n + +log4j.rootLogger=WARN, stdout +log4j.logger.org.apache.hadoop.conf=ERROR +log4j.logger.org.apache.hadoop.crytpo.key.kms.server=ALL +log4j.logger.com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator=OFF +log4j.logger.org.apache.hadoop.security=OFF +log4j.logger.org.apache.directory.server.core=OFF +log4j.logger.org.apache.hadoop.util.NativeCodeLoader=OFF \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/resources/mini-kms-acls-default.xml ---------------------------------------------------------------------- diff --git a/kms/src/main/resources/mini-kms-acls-default.xml b/kms/src/main/resources/mini-kms-acls-default.xml new file mode 100644 index 0000000..24a46b8 --- /dev/null +++ b/kms/src/main/resources/mini-kms-acls-default.xml @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed 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. +--> +<configuration> + + <!-- This file is hot-reloaded when it changes --> + + <!-- KMS ACLs --> + + <property> + <name>hadoop.kms.acl.CREATE</name> + <value>*</value> + <description> + ACL for create-key operations. + If the user does is not in the GET ACL, the key material is not returned + as part of the response. + </description> + </property> + + <property> + <name>hadoop.kms.acl.DELETE</name> + <value>*</value> + <description> + ACL for delete-key operations. + </description> + </property> + + <property> + <name>hadoop.kms.acl.ROLLOVER</name> + <value>*</value> + <description> + ACL for rollover-key operations. + If the user does is not in the GET ACL, the key material is not returned + as part of the response. + </description> + </property> + + <property> + <name>hadoop.kms.acl.GET</name> + <value>*</value> + <description> + ACL for get-key-version and get-current-key operations. + </description> + </property> + + <property> + <name>hadoop.kms.acl.GET_KEYS</name> + <value>*</value> + <description> + ACL for get-keys operation. + </description> + </property> + + <property> + <name>hadoop.kms.acl.GET_METADATA</name> + <value>*</value> + <description> + ACL for get-key-metadata an get-keys-metadata operations. + </description> + </property> + + <property> + <name>hadoop.kms.acl.SET_KEY_MATERIAL</name> + <value>*</value> + <description> + Complimentary ACL for CREATE and ROLLOVER operation to allow the client + to provide the key material when creating or rolling a key. + </description> + </property> + + <property> + <name>hadoop.kms.acl.GENERATE_EEK</name> + <value>*</value> + <description> + ACL for generateEncryptedKey CryptoExtension operations + </description> + </property> + + <property> + <name>hadoop.kms.acl.DECRYPT_EEK</name> + <value>*</value> + <description> + ACL for decrypt EncryptedKey CryptoExtension operations + </description> + </property> + + <property> + <name>default.key.acl.MANAGEMENT</name> + <value>*</value> + <description> + default ACL for MANAGEMENT operations for all key acls that are not + explicitly defined. + </description> + </property> + + <property> + <name>default.key.acl.GENERATE_EEK</name> + <value>*</value> + <description> + default ACL for GENERATE_EEK operations for all key acls that are not + explicitly defined. + </description> + </property> + + <property> + <name>default.key.acl.DECRYPT_EEK</name> + <value>*</value> + <description> + default ACL for DECRYPT_EEK operations for all key acls that are not + explicitly defined. + </description> + </property> + + <property> + <name>default.key.acl.READ</name> + <value>*</value> + <description> + default ACL for READ operations for all key acls that are not + explicitly defined. + </description> + </property> + + +</configuration> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/kms/src/main/webapp/WEB-INF/web.xml b/kms/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..7d87e90 --- /dev/null +++ b/kms/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"> + + <display-name>ranger-kms</display-name> + + <listener> + <listener-class>org.apache.hadoop.crypto.key.kms.server.KMSWebApp</listener-class> + </listener> + + <servlet> + <servlet-name>webservices-driver</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.packages</param-name> + <param-value>org.apache.hadoop.crypto.key.kms.server</param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <!-- <servlet> + <servlet-name>RangerKMSStartUp</servlet-name> + <servlet-class>org.apache.ranger.kms.biz.RangerKMSStartUp</servlet-class> + <load-on-startup>2</load-on-startup> + </servlet> --> + + <servlet> + <servlet-name>jmx-servlet</servlet-name> + <servlet-class>org.apache.hadoop.crypto.key.kms.server.KMSJMXServlet</servlet-class> + </servlet> + + <servlet-mapping> + <servlet-name>webservices-driver</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>jmx-servlet</servlet-name> + <url-pattern>/jmx</url-pattern> + </servlet-mapping> + + <filter> + <filter-name>authFilter</filter-name> + <filter-class>org.apache.hadoop.crypto.key.kms.server.KMSAuthenticationFilter</filter-class> + </filter> + + <filter> + <filter-name>MDCFilter</filter-name> + <filter-class>org.apache.hadoop.crypto.key.kms.server.KMSMDCFilter</filter-class> + </filter> + + <filter-mapping> + <filter-name>authFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <filter-mapping> + <filter-name>MDCFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + +</web-app> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/MiniKMS.java ---------------------------------------------------------------------- diff --git a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/MiniKMS.java b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/MiniKMS.java new file mode 100644 index 0000000..4f802cc --- /dev/null +++ b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/MiniKMS.java @@ -0,0 +1,238 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +import com.google.common.base.Preconditions; +import org.apache.commons.io.IOUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.kms.KMSRESTConstants; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.ssl.SslSocketConnectorSecure; +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.security.SslSocketConnector; +import org.mortbay.jetty.webapp.WebAppContext; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.UUID; + +public class MiniKMS { + + private static Server createJettyServer(String keyStore, String password, int inPort) { + try { + boolean ssl = keyStore != null; + InetAddress localhost = InetAddress.getByName("localhost"); + String host = "localhost"; + ServerSocket ss = new ServerSocket((inPort < 0) ? 0 : inPort, 50, localhost); + int port = ss.getLocalPort(); + ss.close(); + Server server = new Server(0); + if (!ssl) { + server.getConnectors()[0].setHost(host); + server.getConnectors()[0].setPort(port); + } else { + SslSocketConnector c = new SslSocketConnectorSecure(); + c.setHost(host); + c.setPort(port); + c.setNeedClientAuth(false); + c.setKeystore(keyStore); + c.setKeystoreType("jks"); + c.setKeyPassword(password); + server.setConnectors(new Connector[]{c}); + } + return server; + } catch (Exception ex) { + throw new RuntimeException("Could not start embedded servlet container, " + + ex.getMessage(), ex); + } + } + + private static URL getJettyURL(Server server) { + boolean ssl = server.getConnectors()[0].getClass() + == SslSocketConnectorSecure.class; + try { + String scheme = (ssl) ? "https" : "http"; + return new URL(scheme + "://" + + server.getConnectors()[0].getHost() + ":" + + server.getConnectors()[0].getPort()); + } catch (MalformedURLException ex) { + throw new RuntimeException("It should never happen, " + ex.getMessage(), + ex); + } + } + + public static class Builder { + private File kmsConfDir; + private String log4jConfFile; + private File keyStoreFile; + private String keyStorePassword; + private int inPort = -1; + + public Builder() { + kmsConfDir = new File("target/test-classes").getAbsoluteFile(); + log4jConfFile = "kms-log4j.properties"; + } + + public Builder setKmsConfDir(File confDir) { + Preconditions.checkNotNull(confDir, "KMS conf dir is NULL"); + Preconditions.checkArgument(confDir.exists(), + "KMS conf dir does not exist"); + kmsConfDir = confDir; + return this; + } + + public Builder setLog4jConfFile(String log4jConfFile) { + Preconditions.checkNotNull(log4jConfFile, "log4jconf file is NULL"); + this.log4jConfFile = log4jConfFile; + return this; + } + + public Builder setPort(int port) { + Preconditions.checkArgument(port > 0, "input port must be greater than 0"); + this.inPort = port; + return this; + } + + public Builder setSslConf(File keyStoreFile, String keyStorePassword) { + Preconditions.checkNotNull(keyStoreFile, "keystore file is NULL"); + Preconditions.checkNotNull(keyStorePassword, "keystore password is NULL"); + Preconditions.checkArgument(keyStoreFile.exists(), + "keystore file does not exist"); + this.keyStoreFile = keyStoreFile; + this.keyStorePassword = keyStorePassword; + return this; + } + + public MiniKMS build() { + Preconditions.checkArgument(kmsConfDir.exists(), + "KMS conf dir does not exist"); + return new MiniKMS(kmsConfDir.getAbsolutePath(), log4jConfFile, + (keyStoreFile != null) ? keyStoreFile.getAbsolutePath() : null, + keyStorePassword, inPort); + } + } + + private String kmsConfDir; + private String log4jConfFile; + private String keyStore; + private String keyStorePassword; + private Server jetty; + private int inPort; + private URL kmsURL; + + public MiniKMS(String kmsConfDir, String log4ConfFile, String keyStore, + String password, int inPort) { + this.kmsConfDir = kmsConfDir; + this.log4jConfFile = log4ConfFile; + this.keyStore = keyStore; + this.keyStorePassword = password; + this.inPort = inPort; + } + + public void start() throws Exception { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + System.setProperty(KMSConfiguration.KMS_CONFIG_DIR, kmsConfDir); + File aclsFile = new File(kmsConfDir, "kms-acls.xml"); + if (!aclsFile.exists()) { + InputStream is = cl.getResourceAsStream("mini-kms-acls-default.xml"); + OutputStream os = new FileOutputStream(aclsFile); + IOUtils.copy(is, os); + is.close(); + os.close(); + } + File coreFile = new File(kmsConfDir, "core-site.xml"); + if (!coreFile.exists()) { + Configuration core = new Configuration(); + Writer writer = new FileWriter(coreFile); + core.writeXml(writer); + writer.close(); + } + File kmsFile = new File(kmsConfDir, "kms-site.xml"); + if (!kmsFile.exists()) { + Configuration kms = new Configuration(false); + kms.set(KMSConfiguration.KEY_PROVIDER_URI, + "jceks://file@" + new Path(kmsConfDir, "kms.keystore").toUri()); + kms.set("hadoop.kms.authentication.type", "simple"); + Writer writer = new FileWriter(kmsFile); + kms.writeXml(writer); + writer.close(); + } + System.setProperty("log4j.configuration", log4jConfFile); + jetty = createJettyServer(keyStore, keyStorePassword, inPort); + + // we need to do a special handling for MiniKMS to work when in a dir and + // when in a JAR in the classpath thanks to Jetty way of handling of webapps + // when they are in the a DIR, WAR or JAR. + URL webXmlUrl = cl.getResource("kms-webapp/WEB-INF/web.xml"); + if (webXmlUrl == null) { + throw new RuntimeException( + "Could not find kms-webapp/ dir in test classpath"); + } + boolean webXmlInJar = webXmlUrl.getPath().contains(".jar!/"); + String webappPath; + if (webXmlInJar) { + File webInf = new File("target/" + UUID.randomUUID().toString() + + "/kms-webapp/WEB-INF"); + webInf.mkdirs(); + new File(webInf, "web.xml").delete(); + InputStream is = cl.getResourceAsStream("kms-webapp/WEB-INF/web.xml"); + OutputStream os = new FileOutputStream(new File(webInf, "web.xml")); + IOUtils.copy(is, os); + is.close(); + os.close(); + webappPath = webInf.getParentFile().getAbsolutePath(); + } else { + webappPath = cl.getResource("kms-webapp").getPath(); + } + WebAppContext context = new WebAppContext(webappPath, "/kms"); + if (webXmlInJar) { + context.setClassLoader(cl); + } + jetty.addHandler(context); + jetty.start(); + kmsURL = new URL(getJettyURL(jetty), "kms"); + } + + public URL getKMSUrl() { + return kmsURL; + } + + public void stop() { + if (jetty != null && jetty.isRunning()) { + try { + jetty.stop(); + jetty = null; + } catch (Exception ex) { + throw new RuntimeException("Could not stop MiniKMS embedded Jetty, " + + ex.getMessage(), ex); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/bb0bdced/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java ---------------------------------------------------------------------- diff --git a/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java new file mode 100644 index 0000000..12945d7 --- /dev/null +++ b/kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/TestKMSACLs.java @@ -0,0 +1,52 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.crypto.key.kms.server; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.crypto.key.kms.server.KMSACLsType.Type; +import org.apache.hadoop.security.UserGroupInformation; +import org.junit.Assert; +import org.junit.Test; + +public class TestKMSACLs { + + @Test + public void testDefaults() { + KMSACLs acls = new KMSACLs(new Configuration(false)); + for (Type type : Type.values()) { + Assert.assertTrue(acls.hasAccess(type, + UserGroupInformation.createRemoteUser("foo"))); + } + } + + @Test + public void testCustom() { + Configuration conf = new Configuration(false); + for (Type type : Type.values()) { + conf.set(type.getAclConfigKey(), type.toString() + " "); + } + KMSACLs acls = new KMSACLs(conf); + for (Type type : Type.values()) { + Assert.assertTrue(acls.hasAccess(type, + UserGroupInformation.createRemoteUser(type.toString()))); + Assert.assertFalse(acls.hasAccess(type, + UserGroupInformation.createRemoteUser("foo"))); + } + } + +}
