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 java.net.InetSocketAddress; 020 import java.net.ServerSocket; 021 import java.nio.channels.ServerSocketChannel; 022 023 import org.apache.commons.daemon.Daemon; 024 import org.apache.commons.daemon.DaemonContext; 025 import org.apache.hadoop.conf.Configuration; 026 027 import org.apache.hadoop.hdfs.DFSConfigKeys; 028 import org.apache.hadoop.hdfs.server.common.HdfsServerConstants; 029 import org.apache.hadoop.http.HttpServer; 030 import org.apache.hadoop.security.UserGroupInformation; 031 import org.mortbay.jetty.nio.SelectChannelConnector; 032 033 /** 034 * Utility class to start a datanode in a secure cluster, first obtaining 035 * privileged resources before main startup and handing them to the datanode. 036 */ 037 public class SecureDataNodeStarter implements Daemon { 038 /** 039 * Stash necessary resources needed for datanode operation in a secure env. 040 */ 041 public static class SecureResources { 042 private final ServerSocket streamingSocket; 043 private final SelectChannelConnector listener; 044 public SecureResources(ServerSocket streamingSocket, 045 SelectChannelConnector listener) { 046 047 this.streamingSocket = streamingSocket; 048 this.listener = listener; 049 } 050 051 public ServerSocket getStreamingSocket() { return streamingSocket; } 052 053 public SelectChannelConnector getListener() { return listener; } 054 } 055 056 private String [] args; 057 private SecureResources resources; 058 059 @Override 060 public void init(DaemonContext context) throws Exception { 061 System.err.println("Initializing secure datanode resources"); 062 Configuration conf = new Configuration(); 063 064 // Stash command-line arguments for regular datanode 065 args = context.getArguments(); 066 067 // Obtain secure port for data streaming to datanode 068 InetSocketAddress streamingAddr = DataNode.getStreamingAddr(conf); 069 int socketWriteTimeout = conf.getInt(DFSConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY, 070 HdfsServerConstants.WRITE_TIMEOUT); 071 072 ServerSocket ss = (socketWriteTimeout > 0) ? 073 ServerSocketChannel.open().socket() : new ServerSocket(); 074 ss.bind(streamingAddr, 0); 075 076 // Check that we got the port we need 077 if (ss.getLocalPort() != streamingAddr.getPort()) { 078 throw new RuntimeException("Unable to bind on specified streaming port in secure " + 079 "context. Needed " + streamingAddr.getPort() + ", got " + ss.getLocalPort()); 080 } 081 082 // Obtain secure listener for web server 083 SelectChannelConnector listener = 084 (SelectChannelConnector)HttpServer.createDefaultChannelConnector(); 085 InetSocketAddress infoSocAddr = DataNode.getInfoAddr(conf); 086 listener.setHost(infoSocAddr.getHostName()); 087 listener.setPort(infoSocAddr.getPort()); 088 // Open listener here in order to bind to port as root 089 listener.open(); 090 if (listener.getPort() != infoSocAddr.getPort()) { 091 throw new RuntimeException("Unable to bind on specified info port in secure " + 092 "context. Needed " + streamingAddr.getPort() + ", got " + ss.getLocalPort()); 093 } 094 System.err.println("Successfully obtained privileged resources (streaming port = " 095 + ss + " ) (http listener port = " + listener.getConnection() +")"); 096 097 if ((ss.getLocalPort() >= 1023 || listener.getPort() >= 1023) && 098 UserGroupInformation.isSecurityEnabled()) { 099 throw new RuntimeException("Cannot start secure datanode with unprivileged ports"); 100 } 101 System.err.println("Opened streaming server at " + streamingAddr); 102 System.err.println("Opened info server at " + infoSocAddr); 103 resources = new SecureResources(ss, listener); 104 } 105 106 @Override 107 public void start() throws Exception { 108 System.err.println("Starting regular datanode initialization"); 109 DataNode.secureMain(args, resources); 110 } 111 112 @Override public void destroy() { /* Nothing to do */ } 113 @Override public void stop() throws Exception { /* Nothing to do */ } 114 }