| /** |
| * 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.hdfsproxy; |
| |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.HttpURLConnection; |
| import java.net.InetSocketAddress; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.security.KeyStore; |
| import java.security.cert.X509Certificate; |
| import java.util.Date; |
| import java.util.Set; |
| |
| import javax.net.ssl.HostnameVerifier; |
| import javax.net.ssl.HttpsURLConnection; |
| import javax.net.ssl.KeyManager; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.SSLContext; |
| import javax.net.ssl.SSLSession; |
| import javax.net.ssl.TrustManager; |
| import javax.net.ssl.TrustManagerFactory; |
| import javax.net.ssl.X509TrustManager; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.ServletException; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FSDataInputStream; |
| import org.apache.hadoop.fs.FSInputStream; |
| import org.apache.hadoop.io.IOUtils; |
| import org.apache.hadoop.net.NetUtils; |
| import org.apache.hadoop.util.HostsFileReader; |
| import org.apache.hadoop.security.UserGroupInformation; |
| |
| import org.apache.hadoop.hdfs.HdfsConfiguration; |
| |
| /** |
| * Proxy Utility . |
| */ |
| public class ProxyUtil { |
| public static final Log LOG = LogFactory.getLog(ProxyUtil.class); |
| private static final long MM_SECONDS_PER_DAY = 1000 * 60 * 60 * 24; |
| private static final int CERT_EXPIRATION_WARNING_THRESHOLD = 30; // 30 days |
| |
| // warning |
| |
| private static enum UtilityOption { |
| RELOAD("-reloadPermFiles"), GET("-get"), CHECKCERTS( |
| "-checkcerts"); |
| |
| private String name = null; |
| |
| private UtilityOption(String arg) { |
| this.name = arg; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| } |
| |
| /** |
| * Dummy hostname verifier that is used to bypass hostname checking |
| */ |
| private static class DummyHostnameVerifier implements HostnameVerifier { |
| public boolean verify(String hostname, SSLSession session) { |
| return true; |
| } |
| } |
| |
| /** |
| * Dummy trustmanager that is used to bypass server certificate checking |
| */ |
| private static class DummyTrustManager implements X509TrustManager { |
| public void checkClientTrusted(X509Certificate[] chain, String authType) { |
| } |
| |
| public void checkServerTrusted(X509Certificate[] chain, String authType) { |
| } |
| |
| public X509Certificate[] getAcceptedIssuers() { |
| return null; |
| } |
| } |
| |
| private static HttpsURLConnection openConnection(String hostname, int port, |
| String path) throws IOException { |
| try { |
| final URL url = new URI("https", null, hostname, port, path, null, null) |
| .toURL(); |
| HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); |
| // bypass hostname verification |
| conn.setHostnameVerifier(new DummyHostnameVerifier()); |
| conn.setRequestMethod("GET"); |
| return conn; |
| } catch (URISyntaxException e) { |
| throw (IOException) new IOException().initCause(e); |
| } |
| } |
| |
| private static void setupSslProps(Configuration conf) throws IOException { |
| FileInputStream fis = null; |
| try { |
| SSLContext sc = SSLContext.getInstance("SSL"); |
| KeyManager[] kms = null; |
| TrustManager[] tms = null; |
| if (conf.get("ssl.client.keystore.location") != null) { |
| // initialize default key manager with keystore file and pass |
| KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); |
| KeyStore ks = KeyStore.getInstance(conf.get("ssl.client.keystore.type", |
| "JKS")); |
| char[] ksPass = conf.get("ssl.client.keystore.password", "changeit") |
| .toCharArray(); |
| fis = new FileInputStream(conf.get("ssl.client.keystore.location", |
| "keystore.jks")); |
| ks.load(fis, ksPass); |
| kmf.init(ks, conf.get("ssl.client.keystore.keypassword", "changeit") |
| .toCharArray()); |
| kms = kmf.getKeyManagers(); |
| fis.close(); |
| fis = null; |
| } |
| // initialize default trust manager with keystore file and pass |
| if (conf.getBoolean("ssl.client.do.not.authenticate.server", false)) { |
| // by pass trustmanager validation |
| tms = new DummyTrustManager[] { new DummyTrustManager() }; |
| } else { |
| TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); |
| KeyStore ts = KeyStore.getInstance(conf.get( |
| "ssl.client.truststore.type", "JKS")); |
| char[] tsPass = conf.get("ssl.client.truststore.password", "changeit") |
| .toCharArray(); |
| fis = new FileInputStream(conf.get("ssl.client.truststore.location", |
| "truststore.jks")); |
| ts.load(fis, tsPass); |
| tmf.init(ts); |
| tms = tmf.getTrustManagers(); |
| } |
| sc.init(kms, tms, new java.security.SecureRandom()); |
| HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); |
| } catch (Exception e) { |
| throw new IOException("Could not initialize SSLContext", e); |
| } finally { |
| if (fis != null) { |
| fis.close(); |
| } |
| } |
| } |
| |
| static InetSocketAddress getSslAddr(Configuration conf) throws IOException { |
| String addr = conf.get("hdfsproxy.https.address"); |
| if (addr == null) |
| throw new IOException("HdfsProxy address is not specified"); |
| return NetUtils.createSocketAddr(addr); |
| } |
| |
| static boolean sendCommand(Configuration conf, String path) |
| throws IOException { |
| setupSslProps(conf); |
| int sslPort = getSslAddr(conf).getPort(); |
| int err = 0; |
| StringBuilder b = new StringBuilder(); |
| |
| HostsFileReader hostsReader = new HostsFileReader(conf.get( |
| "hdfsproxy.hosts", "hdfsproxy-hosts"), ""); |
| Set<String> hostsList = hostsReader.getHosts(); |
| for (String hostname : hostsList) { |
| HttpsURLConnection connection = null; |
| try { |
| connection = openConnection(hostname, sslPort, path); |
| connection.connect(); |
| if (LOG.isDebugEnabled()) { |
| StringBuilder sb = new StringBuilder(); |
| X509Certificate[] clientCerts = (X509Certificate[]) connection |
| .getLocalCertificates(); |
| if (clientCerts != null) { |
| for (X509Certificate cert : clientCerts) |
| sb.append("\n Client certificate Subject Name is " |
| + cert.getSubjectX500Principal().getName()); |
| } else { |
| sb.append("\n No client certificates were found"); |
| } |
| X509Certificate[] serverCerts = (X509Certificate[]) connection |
| .getServerCertificates(); |
| if (serverCerts != null) { |
| for (X509Certificate cert : serverCerts) |
| sb.append("\n Server certificate Subject Name is " |
| + cert.getSubjectX500Principal().getName()); |
| } else { |
| sb.append("\n No server certificates were found"); |
| } |
| LOG.debug(sb.toString()); |
| } |
| if (connection.getResponseCode() != HttpServletResponse.SC_OK) { |
| b.append("\n\t" + hostname + ": " + connection.getResponseCode() |
| + " " + connection.getResponseMessage()); |
| err++; |
| } |
| } catch (IOException e) { |
| b.append("\n\t" + hostname + ": " + e.getLocalizedMessage()); |
| if (LOG.isDebugEnabled()) |
| LOG.debug("Exception happend for host " + hostname, e); |
| err++; |
| } finally { |
| if (connection != null) |
| connection.disconnect(); |
| } |
| } |
| if (err > 0) { |
| System.err.print("Command failed on the following " + err + " host" |
| + (err == 1 ? ":" : "s:") + b.toString() + "\n"); |
| return false; |
| } |
| return true; |
| } |
| |
| static FSDataInputStream open(Configuration conf, String hostname, int port, |
| String path) throws IOException { |
| setupSslProps(conf); |
| HttpURLConnection connection = null; |
| connection = openConnection(hostname, port, path); |
| connection.connect(); |
| final InputStream in = connection.getInputStream(); |
| return new FSDataInputStream(new FSInputStream() { |
| public int read() throws IOException { |
| return in.read(); |
| } |
| |
| public int read(byte[] b, int off, int len) throws IOException { |
| return in.read(b, off, len); |
| } |
| |
| public void close() throws IOException { |
| in.close(); |
| } |
| |
| public void seek(long pos) throws IOException { |
| throw new IOException("Can't seek!"); |
| } |
| |
| public long getPos() throws IOException { |
| throw new IOException("Position unknown!"); |
| } |
| |
| public boolean seekToNewSource(long targetPos) throws IOException { |
| return false; |
| } |
| }); |
| } |
| |
| static void checkServerCertsExpirationDays(Configuration conf, |
| String hostname, int port) throws IOException { |
| setupSslProps(conf); |
| HttpsURLConnection connection = null; |
| connection = openConnection(hostname, port, null); |
| connection.connect(); |
| X509Certificate[] serverCerts = (X509Certificate[]) connection |
| .getServerCertificates(); |
| Date curDate = new Date(); |
| long curTime = curDate.getTime(); |
| if (serverCerts != null) { |
| for (X509Certificate cert : serverCerts) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("\n Server certificate Subject Name: " |
| + cert.getSubjectX500Principal().getName()); |
| Date expDate = cert.getNotAfter(); |
| long expTime = expDate.getTime(); |
| int dayOffSet = (int) ((expTime - curTime) / MM_SECONDS_PER_DAY); |
| sb.append(" have " + dayOffSet + " days to expire"); |
| if (dayOffSet < CERT_EXPIRATION_WARNING_THRESHOLD) |
| LOG.warn(sb.toString()); |
| else |
| LOG.info(sb.toString()); |
| } |
| } else { |
| LOG.info("\n No Server certs was found"); |
| } |
| |
| if (connection != null) { |
| connection.disconnect(); |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| if (args.length < 1 |
| || (!UtilityOption.RELOAD.getName().equalsIgnoreCase(args[0]) |
| && !UtilityOption.GET.getName().equalsIgnoreCase(args[0]) && !UtilityOption.CHECKCERTS |
| .getName().equalsIgnoreCase(args[0])) |
| || (UtilityOption.GET.getName().equalsIgnoreCase(args[0]) && args.length != 4) |
| || (UtilityOption.CHECKCERTS.getName().equalsIgnoreCase(args[0]) && args.length != 3)) { |
| System.err.println("Usage: ProxyUtil [" + UtilityOption.RELOAD.getName() |
| + "] | [" |
| + UtilityOption.GET.getName() + " <hostname> <#port> <path> ] | [" |
| + UtilityOption.CHECKCERTS.getName() + " <hostname> <#port> ]"); |
| System.exit(0); |
| } |
| Configuration conf = new HdfsConfiguration(false); |
| conf.addResource("ssl-client.xml"); |
| conf.addResource("hdfsproxy-default.xml"); |
| |
| if (UtilityOption.RELOAD.getName().equalsIgnoreCase(args[0])) { |
| // reload user-certs.xml and user-permissions.xml files |
| sendCommand(conf, "/reloadPermFiles"); |
| } else if (UtilityOption.CHECKCERTS.getName().equalsIgnoreCase(args[0])) { |
| checkServerCertsExpirationDays(conf, args[1], Integer.parseInt(args[2])); |
| } else { |
| String hostname = args[1]; |
| int port = Integer.parseInt(args[2]); |
| String path = args[3]; |
| InputStream in = open(conf, hostname, port, path); |
| IOUtils.copyBytes(in, System.out, conf, false); |
| in.close(); |
| } |
| } |
| |
| public static String getNamenode(Configuration conf) |
| throws ServletException { |
| String namenode = conf.get("fs.default.name"); |
| if (namenode == null) { |
| throw new |
| ServletException("Proxy source cluster name node address missing"); |
| } |
| return namenode; |
| } |
| |
| public static UserGroupInformation getProxyUGIFor(String userID) { |
| try { |
| return UserGroupInformation. |
| createProxyUser(userID, UserGroupInformation.getLoginUser()); |
| } catch (IOException e) { |
| throw new |
| RuntimeException("Unable get current logged in user", e); |
| } |
| } |
| |
| |
| } |