ProxyFilter.java
上传用户:quxuerui
上传日期:2018-01-08
资源大小:41811k
文件大小:12k
源码类别:

网格计算

开发平台:

Java

  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.hdfsproxy;
  19. import java.io.IOException;
  20. import java.math.BigInteger;
  21. import java.security.cert.X509Certificate;
  22. import java.security.cert.CertificateExpiredException;
  23. import java.security.cert.CertificateNotYetValidException;
  24. import java.util.Enumeration;
  25. import java.util.HashMap;
  26. import java.util.HashSet;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import java.util.regex.Pattern;
  30. import javax.servlet.Filter;
  31. import javax.servlet.FilterChain;
  32. import javax.servlet.FilterConfig;
  33. import javax.servlet.http.HttpServletRequest;
  34. import javax.servlet.http.HttpServletResponse;
  35. import javax.servlet.ServletException;
  36. import javax.servlet.ServletRequest;
  37. import javax.servlet.ServletResponse;
  38. import org.apache.commons.logging.Log;
  39. import org.apache.commons.logging.LogFactory;
  40. import org.apache.hadoop.conf.Configuration;
  41. import org.apache.hadoop.fs.Path;
  42. import org.apache.hadoop.security.UnixUserGroupInformation;
  43. public class ProxyFilter implements Filter {
  44.   public static final Log LOG = LogFactory.getLog(ProxyFilter.class);
  45.   /** Pattern for triggering reload of user permissions */
  46.   protected static final Pattern RELOAD_PATTERN = Pattern
  47.       .compile("^(/reloadPermFiles)$");
  48.   /** Pattern for triggering clearing of ugi Cache */
  49.   protected static final Pattern CLEAR_PATTERN = Pattern
  50.       .compile("^(/clearUgiCache)$");
  51.   /** Pattern for a filter to find out if a request is HFTP/HSFTP request */
  52.   protected static final Pattern HFTP_PATTERN = Pattern
  53.       .compile("^(/listPaths|/data|/streamFile)$");
  54.   /**
  55.    * Pattern for a filter to find out if an HFTP/HSFTP request stores its file
  56.    * path in the extra path information associated with the URL; if not, the
  57.    * file path is stored in request parameter "filename"
  58.    */
  59.   protected static final Pattern FILEPATH_PATTERN = Pattern
  60.       .compile("^(/listPaths|/data)$");
  61.   private static volatile Map<String, Set<Path>> permsMap;
  62.   private static volatile Map<String, Set<BigInteger>> certsMap;
  63.   static {
  64.     Configuration conf = new Configuration(false);
  65.     conf.addResource("hdfsproxy-default.xml");
  66.     Map<String, Set<Path>> pMap = getPermMap(conf);
  67.     permsMap = pMap != null ? pMap : new HashMap<String, Set<Path>>();
  68.     Map<String, Set<BigInteger>> cMap = getCertsMap(conf);
  69.     certsMap = cMap != null ? cMap : new HashMap<String, Set<BigInteger>>();
  70.   }
  71.   /** {@inheritDoc} */
  72.   public void init(FilterConfig filterConfig) throws ServletException {
  73.   }
  74.   private static Map<String, Set<Path>> getPermMap(Configuration conf) {
  75.     String permLoc = conf.get("hdfsproxy.user.permissions.file.location",
  76.         "user-permissions.xml");
  77.     if (conf.getResource(permLoc) == null) {
  78.       LOG.warn("HdfsProxy user permissions file not found");
  79.       return null;
  80.     }
  81.     Configuration permConf = new Configuration(false);
  82.     permConf.addResource(permLoc);
  83.     Map<String, Set<Path>> map = new HashMap<String, Set<Path>>();
  84.     for (Map.Entry<String, String> e : permConf) {
  85.       String k = e.getKey();
  86.       String v = e.getValue();
  87.       if (k != null && k.length() != 0 && v != null && v.length() != 0) {
  88.         Set<Path> pathSet = new HashSet<Path>();
  89.         String[] paths = v.split(",\s*");
  90.         for (String p : paths) {
  91.           if (p.length() != 0) {
  92.             pathSet.add(new Path(p));
  93.           }
  94.         }
  95.         map.put(k, pathSet);
  96.       }
  97.     }
  98.     return map;
  99.   }
  100.   private static Map<String, Set<BigInteger>> getCertsMap(Configuration conf) {
  101.     String certsLoc = conf.get("hdfsproxy.user.certs.file.location",
  102.         "user-certs.xml");
  103.     if (conf.getResource(certsLoc) == null) {
  104.       LOG.warn("HdfsProxy user certs file not found");
  105.       return null;
  106.     }
  107.     Configuration certsConf = new Configuration(false);
  108.     certsConf.addResource(certsLoc);
  109.     Map<String, Set<BigInteger>> map = new HashMap<String, Set<BigInteger>>();
  110.     for (Map.Entry<String, String> e : certsConf) {
  111.       String k = e.getKey();
  112.       String v = e.getValue().trim();
  113.       if (k != null && k.length() != 0 && v != null && v.length() != 0) {
  114.         Set<BigInteger> numSet = new HashSet<BigInteger>();
  115.         String[] serialnumbers = v.split("\s*,\s*");
  116.         for (String num : serialnumbers) {
  117.           if (num.length() != 0) {
  118.             numSet.add(new BigInteger(num, 16));
  119.           }
  120.         }
  121.         map.put(k, numSet);
  122.       }
  123.     }
  124.     return map;
  125.   }
  126.   /** {@inheritDoc} */
  127.   public void destroy() {
  128.   }
  129.   /** {@inheritDoc} */
  130.   public void doFilter(ServletRequest request, ServletResponse response,
  131.       FilterChain chain) throws IOException, ServletException {
  132.     HttpServletRequest rqst = (HttpServletRequest) request;
  133.     HttpServletResponse rsp = (HttpServletResponse) response;
  134.     if (LOG.isDebugEnabled()) {
  135.       StringBuilder b = new StringBuilder("Request from ").append(
  136.           rqst.getRemoteHost()).append("/").append(rqst.getRemoteAddr())
  137.           .append(":").append(rqst.getRemotePort());
  138.       @SuppressWarnings("unchecked")
  139.       Enumeration<String> e = rqst.getAttributeNames();
  140.       for (; e.hasMoreElements();) {
  141.         String attribute = e.nextElement();
  142.         b.append("n  " + attribute + " => " + rqst.getAttribute(attribute));
  143.       }
  144.       X509Certificate[] userCerts = (X509Certificate[]) rqst
  145.           .getAttribute("javax.servlet.request.X509Certificate");
  146.       if (userCerts != null)
  147.         for (X509Certificate cert : userCerts)
  148.           b.append("n Client certificate Subject Name is "
  149.               + cert.getSubjectX500Principal().getName());
  150.       b.append("n The Scheme is " + rqst.getScheme());
  151.       b.append("n The Auth Type is " + rqst.getAuthType());
  152.       b.append("n The Path Info is " + rqst.getPathInfo());
  153.       b.append("n The Translated Path Info is " + rqst.getPathTranslated());
  154.       b.append("n The Context Path is " + rqst.getContextPath());
  155.       b.append("n The Query String is " + rqst.getQueryString());
  156.       b.append("n The Remote User is " + rqst.getRemoteUser());
  157.       b.append("n The User Principal is " + rqst.getUserPrincipal());
  158.       b.append("n The Request URI is " + rqst.getRequestURI());
  159.       b.append("n The Request URL is " + rqst.getRequestURL());
  160.       b.append("n The Servlet Path is " + rqst.getServletPath());
  161.       LOG.debug(b.toString());
  162.     }
  163.     if (rqst.getScheme().equalsIgnoreCase("https")) {
  164.       boolean isAuthorized = false;
  165.       X509Certificate[] certs = (X509Certificate[]) rqst
  166.           .getAttribute("javax.servlet.request.X509Certificate");
  167.       if (certs == null || certs.length == 0) {
  168.         rsp.sendError(HttpServletResponse.SC_BAD_REQUEST,
  169.             "No client SSL certificate received");
  170.         return;
  171.       }
  172.       for (X509Certificate cert : certs) {
  173.         try {
  174.           cert.checkValidity();
  175.         } catch (CertificateExpiredException e) {
  176.           LOG.info("Received cert for "
  177.               + cert.getSubjectX500Principal().getName() + " expired");
  178.           rsp
  179.               .sendError(HttpServletResponse.SC_FORBIDDEN,
  180.                   "Certificate expired");
  181.           return;
  182.         } catch (CertificateNotYetValidException e) {
  183.           LOG.info("Received cert for "
  184.               + cert.getSubjectX500Principal().getName() + " is not yet valid");
  185.           rsp.sendError(HttpServletResponse.SC_FORBIDDEN,
  186.               "Certificate is not yet valid");
  187.           return;
  188.         }
  189.       }
  190.       String[] tokens = certs[0].getSubjectX500Principal().getName().split(
  191.           "\s*,\s*");
  192.       String userID = null;
  193.       for (String s : tokens) {
  194.         if (s.startsWith("CN=")) {
  195.           userID = s;
  196.           break;
  197.         }
  198.       }
  199.       if (userID == null || userID.length() < 4) {
  200.         LOG.info("Can't retrieve user ID from SSL certificate");
  201.         rsp.sendError(HttpServletResponse.SC_FORBIDDEN,
  202.             "Can't retrieve user ID from SSL certificate");
  203.         return;
  204.       }
  205.       userID = userID.substring(3);
  206.       String servletPath = rqst.getServletPath();
  207.       if (HFTP_PATTERN.matcher(servletPath).matches()) {
  208.         // request is an HSFTP request
  209.         if (FILEPATH_PATTERN.matcher(servletPath).matches()) {
  210.           // file path as part of the URL
  211.           isAuthorized = checkPath(userID, certs[0],
  212.               rqst.getPathInfo() != null ? rqst.getPathInfo() : "/");
  213.         } else {
  214.           // file path is stored in "filename" parameter
  215.           isAuthorized = checkPath(userID, certs[0], rqst
  216.               .getParameter("filename"));
  217.         }
  218.       } else if (RELOAD_PATTERN.matcher(servletPath).matches()
  219.           && checkUser("Admin", certs[0])) {
  220.         Configuration conf = new Configuration(false);
  221.         conf.addResource("hdfsproxy-default.xml");
  222.         Map<String, Set<Path>> permsMap = getPermMap(conf);
  223.         Map<String, Set<BigInteger>> certsMap = getCertsMap(conf);
  224.         if (permsMap == null || certsMap == null) {
  225.           LOG.warn("Permission files reloading failed");
  226.           rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  227.               "Permission files reloading failed");
  228.           return;
  229.         }
  230.         ProxyFilter.permsMap = permsMap;
  231.         ProxyFilter.certsMap = certsMap;
  232.         LOG.info("User permissions and user certs files reloaded");
  233.         rsp.setStatus(HttpServletResponse.SC_OK);
  234.         return;
  235.       } else if (CLEAR_PATTERN.matcher(servletPath).matches()
  236.           && checkUser("Admin", certs[0])) {
  237.         ProxyUgiManager.clearCache();
  238.         LOG.info("Ugi cache cleared");
  239.         rsp.setStatus(HttpServletResponse.SC_OK);
  240.         return;
  241.       }
  242.       if (!isAuthorized) {
  243.         rsp.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized access");
  244.         return;
  245.       }
  246.       // request is authorized, set ugi for servlets
  247.       UnixUserGroupInformation ugi = ProxyUgiManager
  248.           .getUgiForUser(userID);
  249.       if (ugi == null) {
  250.         LOG.info("Can't retrieve ugi for user " + userID);
  251.         rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
  252.             "Can't retrieve ugi for user " + userID);
  253.         return;
  254.       }
  255.       rqst.setAttribute("authorized.ugi", ugi);
  256.     } else { // http request, set ugi for servlets, only for testing purposes
  257.       String ugi = rqst.getParameter("ugi");
  258.       rqst.setAttribute("authorized.ugi", new UnixUserGroupInformation(ugi
  259.           .split(",")));
  260.     }
  261.     chain.doFilter(request, response);
  262.   }
  263.   /** check that client's cert is listed in the user certs file */
  264.   private boolean checkUser(String userID, X509Certificate cert) {
  265.     Set<BigInteger> numSet = certsMap.get(userID);
  266.     if (numSet == null) {
  267.       LOG.info("User " + userID + " is not configured in the user certs file");
  268.       return false;
  269.     }
  270.     if (!numSet.contains(cert.getSerialNumber())) {
  271.       LOG.info("Cert with serial number " + cert.getSerialNumber()
  272.           + " is not listed for user " + userID);
  273.       return false;
  274.     }
  275.     return true;
  276.   }
  277.   /** check that the requested path is listed in the user permissions file */
  278.   private boolean checkPath(String userID, X509Certificate cert, String pathInfo) {
  279.     if (!checkUser(userID, cert)) {
  280.       return false;
  281.     }
  282.     Set<Path> pathSet = permsMap.get(userID);
  283.     if (pathSet == null) {
  284.       LOG.info("User " + userID
  285.               + " is not listed in the user permissions file");
  286.       return false;
  287.     }
  288.     if (pathInfo == null || pathInfo.length() == 0) {
  289.       LOG.info("Can't get file path from HTTPS request; user is " + userID);
  290.       return false;
  291.     }
  292.     Path userPath = new Path(pathInfo);
  293.     while (userPath != null) {
  294.       if (LOG.isDebugEnabled()) {
  295.         LOG.debug("n Checking file path " + userPath);
  296.       }
  297.       if (pathSet.contains(userPath))
  298.         return true;
  299.       userPath = userPath.getParent();
  300.     }
  301.     LOG.info("User " + userID + " is not authorized to access " + pathInfo);
  302.     return false;
  303.   }
  304. }