001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with this 004 * work for additional information regarding copyright ownership. The ASF 005 * licenses this file to you under the Apache License, Version 2.0 (the 006 * "License"); you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations under 015 * the License. 016 */ 017 package org.apache.hadoop.hdfs.server.datanode; 018 019 import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION; 020 021 import java.net.InetSocketAddress; 022 import java.net.ServerSocket; 023 import java.nio.channels.ServerSocketChannel; 024 025 import org.apache.commons.daemon.Daemon; 026 import org.apache.commons.daemon.DaemonContext; 027 import org.apache.hadoop.conf.Configuration; 028 029 import org.apache.hadoop.hdfs.DFSConfigKeys; 030 import org.apache.hadoop.hdfs.server.common.HdfsServerConstants; 031 import org.apache.hadoop.http.HttpServer; 032 import org.mortbay.jetty.nio.SelectChannelConnector; 033 034 /** 035 * Utility class to start a datanode in a secure cluster, first obtaining 036 * privileged resources before main startup and handing them to the datanode. 037 */ 038 public class SecureDataNodeStarter implements Daemon { 039 /** 040 * Stash necessary resources needed for datanode operation in a secure env. 041 */ 042 public static class SecureResources { 043 private final ServerSocket streamingSocket; 044 private final SelectChannelConnector listener; 045 public SecureResources(ServerSocket streamingSocket, 046 SelectChannelConnector listener) { 047 048 this.streamingSocket = streamingSocket; 049 this.listener = listener; 050 } 051 052 public ServerSocket getStreamingSocket() { return streamingSocket; } 053 054 public SelectChannelConnector getListener() { return listener; } 055 } 056 057 private String [] args; 058 private SecureResources resources; 059 060 @Override 061 public void init(DaemonContext context) throws Exception { 062 System.err.println("Initializing secure datanode resources"); 063 // We should only start up a secure datanode in a Kerberos-secured cluster 064 Configuration conf = new Configuration(); // Skip UGI method to not log in 065 if(!conf.get(HADOOP_SECURITY_AUTHENTICATION).equals("kerberos")) 066 throw new RuntimeException("Cannot start secure datanode in unsecure cluster"); 067 068 // Stash command-line arguments for regular datanode 069 args = context.getArguments(); 070 071 // Obtain secure port for data streaming to datanode 072 InetSocketAddress socAddr = DataNode.getStreamingAddr(conf); 073 int socketWriteTimeout = conf.getInt(DFSConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY, 074 HdfsServerConstants.WRITE_TIMEOUT); 075 076 ServerSocket ss = (socketWriteTimeout > 0) ? 077 ServerSocketChannel.open().socket() : new ServerSocket(); 078 ss.bind(socAddr, 0); 079 080 // Check that we got the port we need 081 if(ss.getLocalPort() != socAddr.getPort()) 082 throw new RuntimeException("Unable to bind on specified streaming port in secure " + 083 "context. Needed " + socAddr.getPort() + ", got " + ss.getLocalPort()); 084 085 // Obtain secure listener for web server 086 SelectChannelConnector listener = 087 (SelectChannelConnector)HttpServer.createDefaultChannelConnector(); 088 InetSocketAddress infoSocAddr = DataNode.getInfoAddr(conf); 089 listener.setHost(infoSocAddr.getHostName()); 090 listener.setPort(infoSocAddr.getPort()); 091 // Open listener here in order to bind to port as root 092 listener.open(); 093 if(listener.getPort() != infoSocAddr.getPort()) 094 throw new RuntimeException("Unable to bind on specified info port in secure " + 095 "context. Needed " + socAddr.getPort() + ", got " + ss.getLocalPort()); 096 System.err.println("Successfully obtained privileged resources (streaming port = " 097 + ss + " ) (http listener port = " + listener.getConnection() +")"); 098 099 if(ss.getLocalPort() >= 1023 || listener.getPort() >= 1023) 100 throw new RuntimeException("Cannot start secure datanode with unprivileged ports"); 101 102 resources = new SecureResources(ss, listener); 103 } 104 105 @Override 106 public void start() throws Exception { 107 System.err.println("Starting regular datanode initialization"); 108 DataNode.secureMain(args, resources); 109 } 110 111 @Override public void destroy() { /* Nothing to do */ } 112 @Override public void stop() throws Exception { /* Nothing to do */ } 113 }