Return-Path: Delivered-To: apmail-hadoop-hdfs-commits-archive@minotaur.apache.org Received: (qmail 16924 invoked from network); 10 Jul 2010 19:37:08 -0000 Received: from unknown (HELO mail.apache.org) (140.211.11.3) by 140.211.11.9 with SMTP; 10 Jul 2010 19:37:08 -0000 Received: (qmail 8825 invoked by uid 500); 10 Jul 2010 19:37:08 -0000 Delivered-To: apmail-hadoop-hdfs-commits-archive@hadoop.apache.org Received: (qmail 8770 invoked by uid 500); 10 Jul 2010 19:37:07 -0000 Mailing-List: contact hdfs-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: hdfs-dev@hadoop.apache.org Delivered-To: mailing list hdfs-commits@hadoop.apache.org Received: (qmail 8762 invoked by uid 99); 10 Jul 2010 19:37:07 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 10 Jul 2010 19:37:07 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 10 Jul 2010 19:37:04 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 077182388A1C; Sat, 10 Jul 2010 19:36:11 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r962907 - in /hadoop/hdfs/trunk: ./ src/java/org/apache/hadoop/hdfs/server/namenode/ src/test/unit/org/apache/hadoop/hdfs/server/namenode/ Date: Sat, 10 Jul 2010 19:36:10 -0000 To: hdfs-commits@hadoop.apache.org From: jghoman@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100710193611.077182388A1C@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: jghoman Date: Sat Jul 10 19:36:10 2010 New Revision: 962907 URL: http://svn.apache.org/viewvc?rev=962907&view=rev Log: HDFS-1033. In secure clusters, NN and SNN should verify that the remote principal during image and edits transfer. Added: hadoop/hdfs/trunk/src/test/unit/org/apache/hadoop/hdfs/server/namenode/TestGetImageServlet.java Modified: hadoop/hdfs/trunk/CHANGES.txt hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/SecondaryNameNode.java Modified: hadoop/hdfs/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/hadoop/hdfs/trunk/CHANGES.txt?rev=962907&r1=962906&r2=962907&view=diff ============================================================================== --- hadoop/hdfs/trunk/CHANGES.txt (original) +++ hadoop/hdfs/trunk/CHANGES.txt Sat Jul 10 19:36:10 2010 @@ -18,6 +18,9 @@ Trunk (unreleased changes) HDFS-1006. getImage/putImage http requests should be https for the case of security enabled. (borya and jghoman via jghoman) + HDFS-1033. In secure clusters, NN and SNN should verify that the remote + principal during image and edits transfer. (jghoman) + IMPROVEMENTS HDFS-1096. fix for prev. commit. (boryas) Modified: hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java URL: http://svn.apache.org/viewvc/hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java?rev=962907&r1=962906&r2=962907&view=diff ============================================================================== --- hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java (original) +++ hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/GetImageServlet.java Sat Jul 10 19:36:10 2010 @@ -17,6 +17,11 @@ */ package org.apache.hadoop.hdfs.server.namenode; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_KRB_HTTPS_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_SECONDARY_NAMENODE_KRB_HTTPS_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_SECONDARY_NAMENODE_USER_NAME_KEY; + import java.security.PrivilegedExceptionAction; import java.util.*; import java.io.*; @@ -26,6 +31,9 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hdfs.DFSConfigKeys; @@ -41,6 +49,8 @@ import org.apache.hadoop.util.StringUtil public class GetImageServlet extends HttpServlet { private static final long serialVersionUID = -7669068179452648952L; + private static final Log LOG = LogFactory.getLog(GetImageServlet.class); + @SuppressWarnings("unchecked") public void doGet(final HttpServletRequest request, final HttpServletResponse response @@ -51,9 +61,17 @@ public class GetImageServlet extends Htt final FSImage nnImage = (FSImage)context.getAttribute("name.system.image"); final TransferFsImage ff = new TransferFsImage(pmap, request, response); final Configuration conf = (Configuration)getServletContext().getAttribute("name.conf"); + + if(UserGroupInformation.isSecurityEnabled() && + !isValidRequestor(request.getRemoteUser(), conf)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, + "Only Namenode and Secondary Namenode may access this servlet"); + LOG.warn("Received non-NN/SNN request for image or edits from " + + request.getRemoteHost()); + return; + } - UserGroupInformation.getCurrentUser().doAs(new PrivilegedExceptionAction() { - + UserGroupInformation.getCurrentUser().doAs(new PrivilegedExceptionAction() { @Override public Void run() throws Exception { if (ff.getImage()) { @@ -103,4 +121,25 @@ public class GetImageServlet extends Htt response.getOutputStream().close(); } } + + protected boolean isValidRequestor(String remoteUser, Configuration conf) { + if(remoteUser == null) { // This really shouldn't happen... + LOG.warn("Received null remoteUser while authorizing access to getImage servlet"); + return false; + } + + String [] validRequestors = {conf.get(DFS_NAMENODE_KRB_HTTPS_USER_NAME_KEY), + conf.get(DFS_NAMENODE_USER_NAME_KEY), + conf.get(DFS_SECONDARY_NAMENODE_KRB_HTTPS_USER_NAME_KEY), + conf.get(DFS_SECONDARY_NAMENODE_USER_NAME_KEY) }; + + for(String v : validRequestors) { + if(v != null && v.equals(remoteUser)) { + if(LOG.isDebugEnabled()) LOG.debug("isValidRequestor is allowing: " + remoteUser); + return true; + } + } + if(LOG.isDebugEnabled()) LOG.debug("isValidRequestor is rejecting: " + remoteUser); + return false; + } } Modified: hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/SecondaryNameNode.java URL: http://svn.apache.org/viewvc/hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/SecondaryNameNode.java?rev=962907&r1=962906&r2=962907&view=diff ============================================================================== --- hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/SecondaryNameNode.java (original) +++ hadoop/hdfs/trunk/src/java/org/apache/hadoop/hdfs/server/namenode/SecondaryNameNode.java Sat Jul 10 19:36:10 2010 @@ -115,8 +115,8 @@ public class SecondaryNameNode implement public SecondaryNameNode(Configuration conf) throws IOException { UserGroupInformation.setConfiguration(conf); DFSUtil.login(conf, - DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY, - DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY); + DFSConfigKeys.DFS_SECONDARY_NAMENODE_KEYTAB_FILE_KEY, + DFSConfigKeys.DFS_SECONDARY_NAMENODE_USER_NAME_KEY); try { initialize(conf); Added: hadoop/hdfs/trunk/src/test/unit/org/apache/hadoop/hdfs/server/namenode/TestGetImageServlet.java URL: http://svn.apache.org/viewvc/hadoop/hdfs/trunk/src/test/unit/org/apache/hadoop/hdfs/server/namenode/TestGetImageServlet.java?rev=962907&view=auto ============================================================================== --- hadoop/hdfs/trunk/src/test/unit/org/apache/hadoop/hdfs/server/namenode/TestGetImageServlet.java (added) +++ hadoop/hdfs/trunk/src/test/unit/org/apache/hadoop/hdfs/server/namenode/TestGetImageServlet.java Sat Jul 10 19:36:10 2010 @@ -0,0 +1,68 @@ +/** + * 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.hdfs.server.namenode; + +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_KRB_HTTPS_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_SECONDARY_NAMENODE_KRB_HTTPS_USER_NAME_KEY; +import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_SECONDARY_NAMENODE_USER_NAME_KEY; +import static org.junit.Assert.*; + +import org.apache.hadoop.conf.Configuration; +import org.junit.Test; + +public class TestGetImageServlet { + + // Worker class to poke the isValidRequestor method with verifying it accepts + // or rejects with these standard allowed principals + private void verifyIsValidReqBehavior(GetImageServlet gim, + boolean shouldSucceed, String msg) { + final String [] validRequestors = {DFS_NAMENODE_KRB_HTTPS_USER_NAME_KEY, + DFS_NAMENODE_USER_NAME_KEY, + DFS_SECONDARY_NAMENODE_KRB_HTTPS_USER_NAME_KEY, + DFS_SECONDARY_NAMENODE_USER_NAME_KEY }; + + for(String v : validRequestors) { + Configuration conf = new Configuration(); + conf.set(v, "a"); + assertEquals(msg + v, gim.isValidRequestor(shouldSucceed ? "a" : "b", conf), + shouldSucceed); + } + } + + @Test + public void IsValidRequestorAcceptsCorrectly() { + GetImageServlet gim = new GetImageServlet(); + + verifyIsValidReqBehavior(gim, true, + "isValidRequestor has rejected a valid requestor: "); + } + + @Test + public void IsValidRequestorRejectsCorrectly() { + GetImageServlet gim = new GetImageServlet(); + + // Don't set any valid requestors + assertFalse("isValidRequestor allowed a requestor despite no values being set", + gim.isValidRequestor("not set", new Configuration())); + + verifyIsValidReqBehavior(gim, false, + "isValidRequestor has allowed an invalid requestor: "); + } + +}