1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.hadoop.crypto.key.kms.server; 19 20 import com.codahale.metrics.JmxReporter; 21 import com.codahale.metrics.Meter; 22 import com.codahale.metrics.MetricRegistry; 23 24 import org.apache.hadoop.classification.InterfaceAudience; 25 import org.apache.hadoop.conf.Configuration; 26 import org.apache.hadoop.crypto.key.CachingKeyProvider; 27 import org.apache.hadoop.crypto.key.KeyProvider; 28 import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; 29 import org.apache.hadoop.crypto.key.KeyProviderFactory; 30 import org.apache.hadoop.http.HttpServer2; 31 import org.apache.hadoop.security.authorize.AccessControlList; 32 import org.apache.hadoop.util.VersionInfo; 33 import org.apache.log4j.PropertyConfigurator; 34 import org.slf4j.Logger; 35 import org.slf4j.LoggerFactory; 36 import org.slf4j.bridge.SLF4JBridgeHandler; 37 38 import javax.servlet.ServletContextEvent; 39 import javax.servlet.ServletContextListener; 40 41 import java.io.File; 42 import java.net.URI; 43 import java.net.URL; 44 import java.util.List; 45 46 @InterfaceAudience.Private 47 public class KMSWebApp implements ServletContextListener { 48 49 private static final String LOG4J_PROPERTIES = "kms-log4j.properties"; 50 51 private static final String METRICS_PREFIX = "hadoop.kms."; 52 private static final String ADMIN_CALLS_METER = METRICS_PREFIX + 53 "admin.calls.meter"; 54 private static final String KEY_CALLS_METER = METRICS_PREFIX + 55 "key.calls.meter"; 56 private static final String INVALID_CALLS_METER = METRICS_PREFIX + 57 "invalid.calls.meter"; 58 private static final String UNAUTHORIZED_CALLS_METER = METRICS_PREFIX + 59 "unauthorized.calls.meter"; 60 private static final String UNAUTHENTICATED_CALLS_METER = METRICS_PREFIX + 61 "unauthenticated.calls.meter"; 62 private static final String GENERATE_EEK_METER = METRICS_PREFIX + 63 "generate_eek.calls.meter"; 64 private static final String DECRYPT_EEK_METER = METRICS_PREFIX + 65 "decrypt_eek.calls.meter"; 66 67 private static Logger LOG; 68 private static MetricRegistry metricRegistry; 69 70 private JmxReporter jmxReporter; 71 private static Configuration kmsConf; 72 private static KMSACLs kmsAcls; 73 private static Meter adminCallsMeter; 74 private static Meter keyCallsMeter; 75 private static Meter unauthorizedCallsMeter; 76 private static Meter unauthenticatedCallsMeter; 77 private static Meter decryptEEKCallsMeter; 78 private static Meter generateEEKCallsMeter; 79 private static Meter invalidCallsMeter; 80 private static KMSAudit kmsAudit; 81 private static KeyProviderCryptoExtension keyProviderCryptoExtension; 82 83 static { SLF4JBridgeHandler.removeHandlersForRootLogger()84 SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install()85 SLF4JBridgeHandler.install(); 86 } 87 initLogging(String confDir)88 private void initLogging(String confDir) { 89 if (System.getProperty("log4j.configuration") == null) { 90 System.setProperty("log4j.defaultInitOverride", "true"); 91 boolean fromClasspath = true; 92 File log4jConf = new File(confDir, LOG4J_PROPERTIES).getAbsoluteFile(); 93 if (log4jConf.exists()) { 94 PropertyConfigurator.configureAndWatch(log4jConf.getPath(), 1000); 95 fromClasspath = false; 96 } else { 97 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 98 URL log4jUrl = cl.getResource(LOG4J_PROPERTIES); 99 if (log4jUrl != null) { 100 PropertyConfigurator.configure(log4jUrl); 101 } 102 } 103 LOG = LoggerFactory.getLogger(KMSWebApp.class); 104 LOG.debug("KMS log starting"); 105 if (fromClasspath) { 106 LOG.warn("Log4j configuration file '{}' not found", LOG4J_PROPERTIES); 107 LOG.warn("Logging with INFO level to standard output"); 108 } 109 } else { 110 LOG = LoggerFactory.getLogger(KMSWebApp.class); 111 } 112 } 113 114 @Override contextInitialized(ServletContextEvent sce)115 public void contextInitialized(ServletContextEvent sce) { 116 try { 117 String confDir = System.getProperty(KMSConfiguration.KMS_CONFIG_DIR); 118 if (confDir == null) { 119 throw new RuntimeException("System property '" + 120 KMSConfiguration.KMS_CONFIG_DIR + "' not defined"); 121 } 122 kmsConf = KMSConfiguration.getKMSConf(); 123 initLogging(confDir); 124 LOG.info("-------------------------------------------------------------"); 125 LOG.info(" Java runtime version : {}", System.getProperty( 126 "java.runtime.version")); 127 LOG.info(" KMS Hadoop Version: " + VersionInfo.getVersion()); 128 LOG.info("-------------------------------------------------------------"); 129 130 kmsAcls = new KMSACLs(); 131 kmsAcls.startReloader(); 132 133 metricRegistry = new MetricRegistry(); 134 jmxReporter = JmxReporter.forRegistry(metricRegistry).build(); 135 jmxReporter.start(); 136 generateEEKCallsMeter = metricRegistry.register(GENERATE_EEK_METER, 137 new Meter()); 138 decryptEEKCallsMeter = metricRegistry.register(DECRYPT_EEK_METER, 139 new Meter()); 140 adminCallsMeter = metricRegistry.register(ADMIN_CALLS_METER, new Meter()); 141 keyCallsMeter = metricRegistry.register(KEY_CALLS_METER, new Meter()); 142 invalidCallsMeter = metricRegistry.register(INVALID_CALLS_METER, 143 new Meter()); 144 unauthorizedCallsMeter = metricRegistry.register(UNAUTHORIZED_CALLS_METER, 145 new Meter()); 146 unauthenticatedCallsMeter = metricRegistry.register( 147 UNAUTHENTICATED_CALLS_METER, new Meter()); 148 149 kmsAudit = 150 new KMSAudit(kmsConf.getLong( 151 KMSConfiguration.KMS_AUDIT_AGGREGATION_WINDOW, 152 KMSConfiguration.KMS_AUDIT_AGGREGATION_WINDOW_DEFAULT)); 153 154 // this is required for the the JMXJsonServlet to work properly. 155 // the JMXJsonServlet is behind the authentication filter, 156 // thus the '*' ACL. 157 sce.getServletContext().setAttribute(HttpServer2.CONF_CONTEXT_ATTRIBUTE, 158 kmsConf); 159 sce.getServletContext().setAttribute(HttpServer2.ADMINS_ACL, 160 new AccessControlList(AccessControlList.WILDCARD_ACL_VALUE)); 161 162 // intializing the KeyProvider 163 String providerString = kmsConf.get(KMSConfiguration.KEY_PROVIDER_URI); 164 if (providerString == null) { 165 throw new IllegalStateException("No KeyProvider has been defined"); 166 } 167 KeyProvider keyProvider = 168 KeyProviderFactory.get(new URI(providerString), kmsConf); 169 if (kmsConf.getBoolean(KMSConfiguration.KEY_CACHE_ENABLE, 170 KMSConfiguration.KEY_CACHE_ENABLE_DEFAULT)) { 171 long keyTimeOutMillis = 172 kmsConf.getLong(KMSConfiguration.KEY_CACHE_TIMEOUT_KEY, 173 KMSConfiguration.KEY_CACHE_TIMEOUT_DEFAULT); 174 long currKeyTimeOutMillis = 175 kmsConf.getLong(KMSConfiguration.CURR_KEY_CACHE_TIMEOUT_KEY, 176 KMSConfiguration.CURR_KEY_CACHE_TIMEOUT_DEFAULT); 177 keyProvider = new CachingKeyProvider(keyProvider, keyTimeOutMillis, 178 currKeyTimeOutMillis); 179 } 180 LOG.info("Initialized KeyProvider " + keyProvider); 181 182 keyProviderCryptoExtension = KeyProviderCryptoExtension. 183 createKeyProviderCryptoExtension(keyProvider); 184 keyProviderCryptoExtension = 185 new EagerKeyGeneratorKeyProviderCryptoExtension(kmsConf, 186 keyProviderCryptoExtension); 187 if (kmsConf.getBoolean(KMSConfiguration.KEY_AUTHORIZATION_ENABLE, 188 KMSConfiguration.KEY_AUTHORIZATION_ENABLE_DEFAULT)) { 189 keyProviderCryptoExtension = 190 new KeyAuthorizationKeyProvider( 191 keyProviderCryptoExtension, kmsAcls); 192 } 193 194 LOG.info("Initialized KeyProviderCryptoExtension " 195 + keyProviderCryptoExtension); 196 final int defaultBitlength = kmsConf 197 .getInt(KeyProvider.DEFAULT_BITLENGTH_NAME, 198 KeyProvider.DEFAULT_BITLENGTH); 199 LOG.info("Default key bitlength is {}", defaultBitlength); 200 LOG.info("KMS Started"); 201 } catch (Throwable ex) { 202 System.out.println(); 203 System.out.println("ERROR: Hadoop KMS could not be started"); 204 System.out.println(); 205 System.out.println("REASON: " + ex.toString()); 206 System.out.println(); 207 System.out.println("Stacktrace:"); 208 System.out.println("---------------------------------------------------"); 209 ex.printStackTrace(System.out); 210 System.out.println("---------------------------------------------------"); 211 System.out.println(); 212 System.exit(1); 213 } 214 } 215 216 @Override contextDestroyed(ServletContextEvent sce)217 public void contextDestroyed(ServletContextEvent sce) { 218 kmsAudit.shutdown(); 219 kmsAcls.stopReloader(); 220 jmxReporter.stop(); 221 jmxReporter.close(); 222 metricRegistry = null; 223 LOG.info("KMS Stopped"); 224 } 225 getConfiguration()226 public static Configuration getConfiguration() { 227 return new Configuration(kmsConf); 228 } 229 getACLs()230 public static KMSACLs getACLs() { 231 return kmsAcls; 232 } 233 getAdminCallsMeter()234 public static Meter getAdminCallsMeter() { 235 return adminCallsMeter; 236 } 237 getKeyCallsMeter()238 public static Meter getKeyCallsMeter() { 239 return keyCallsMeter; 240 } 241 getInvalidCallsMeter()242 public static Meter getInvalidCallsMeter() { 243 return invalidCallsMeter; 244 } 245 getGenerateEEKCallsMeter()246 public static Meter getGenerateEEKCallsMeter() { 247 return generateEEKCallsMeter; 248 } 249 getDecryptEEKCallsMeter()250 public static Meter getDecryptEEKCallsMeter() { 251 return decryptEEKCallsMeter; 252 } 253 getUnauthorizedCallsMeter()254 public static Meter getUnauthorizedCallsMeter() { 255 return unauthorizedCallsMeter; 256 } 257 getUnauthenticatedCallsMeter()258 public static Meter getUnauthenticatedCallsMeter() { 259 return unauthenticatedCallsMeter; 260 } 261 getKeyProvider()262 public static KeyProviderCryptoExtension getKeyProvider() { 263 return keyProviderCryptoExtension; 264 } 265 getKMSAudit()266 public static KMSAudit getKMSAudit() { 267 return kmsAudit; 268 } 269 } 270