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