org.apache.hadoop.hdfsproxy.ProxyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfsproxy.ProxyUtil.java

Source

/**
 * 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.ServletException;
import javax.servlet.http.HttpServletResponse;

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.security.UserGroupInformation;
import org.apache.hadoop.util.HostsFileReader;

/**
 * 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()) {
                    StringBuffer sb = new StringBuffer();
                    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) {
                StringBuffer sb = new StringBuffer();
                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 UserGroupInformation getProxyUGIFor(String userID) {
        LOG.debug("Proxying as " + userID);
        try {
            return UserGroupInformation.createProxyUser(userID, UserGroupInformation.getLoginUser());
        } catch (IOException e) {
            throw new RuntimeException("Unable get current logged in user", e);
        }
    }

    public static String getNamenode(Configuration conf) throws ServletException {
        String nn = conf.get("fs.default.name");
        if (nn == null) {
            throw new ServletException("Proxy source cluster name node address not specified");
        }
        return nn;
    }

    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 Configuration(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();
        }
    }

}