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.hdfs.server.namenode;
19 
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertThat;
22 import static org.hamcrest.core.Is.is;
23 import static org.hamcrest.core.IsNot.not;
24 import org.junit.Test;
25 
26 import org.apache.hadoop.fs.FileUtil;
27 import org.apache.hadoop.hdfs.DFSConfigKeys;
28 import org.apache.hadoop.http.HttpConfig;
29 import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
30 
31 import java.io.File;
32 import java.io.IOException;
33 
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.conf.Configuration;
37 import static org.apache.hadoop.hdfs.DFSConfigKeys.*;
38 import static org.junit.Assert.assertTrue;
39 
40 import org.apache.hadoop.hdfs.HdfsConfiguration;
41 import org.apache.hadoop.hdfs.MiniDFSCluster;
42 
43 /**
44  * This test checks that the NameNode respects the following keys:
45  *
46  *  - DFS_NAMENODE_RPC_BIND_HOST_KEY
47  *  - DFS_NAMENODE_SERVICE_RPC_BIND_HOST_KEY
48  *  - DFS_NAMENODE_HTTP_BIND_HOST_KEY
49  *  - DFS_NAMENODE_HTTPS_BIND_HOST_KEY
50 
51  */
52 public class TestNameNodeRespectsBindHostKeys {
53   public static final Log LOG = LogFactory.getLog(TestNameNodeRespectsBindHostKeys.class);
54   private static final String WILDCARD_ADDRESS = "0.0.0.0";
55   private static final String LOCALHOST_SERVER_ADDRESS = "127.0.0.1:0";
56 
getRpcServerAddress(MiniDFSCluster cluster)57   private static String getRpcServerAddress(MiniDFSCluster cluster) {
58     NameNodeRpcServer rpcServer = (NameNodeRpcServer) cluster.getNameNodeRpc();
59     return rpcServer.getClientRpcServer().getListenerAddress().getAddress().toString();
60   }
61 
getServiceRpcServerAddress(MiniDFSCluster cluster)62   private static String getServiceRpcServerAddress(MiniDFSCluster cluster) {
63     NameNodeRpcServer rpcServer = (NameNodeRpcServer) cluster.getNameNodeRpc();
64     return rpcServer.getServiceRpcServer().getListenerAddress().getAddress().toString();
65   }
66 
67   @Test (timeout=300000)
testRpcBindHostKey()68   public void testRpcBindHostKey() throws IOException {
69     Configuration conf = new HdfsConfiguration();
70     MiniDFSCluster cluster = null;
71 
72     LOG.info("Testing without " + DFS_NAMENODE_RPC_BIND_HOST_KEY);
73 
74     // NN should not bind the wildcard address by default.
75     try {
76       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
77       cluster.waitActive();
78       String address = getRpcServerAddress(cluster);
79       assertThat("Bind address not expected to be wildcard by default.",
80                  address, not("/" + WILDCARD_ADDRESS));
81     } finally {
82       if (cluster != null) {
83         cluster.shutdown();
84         cluster = null;
85       }
86     }
87 
88     LOG.info("Testing with " + DFS_NAMENODE_RPC_BIND_HOST_KEY);
89 
90     // Tell NN to bind the wildcard address.
91     conf.set(DFS_NAMENODE_RPC_BIND_HOST_KEY, WILDCARD_ADDRESS);
92 
93     // Verify that NN binds wildcard address now.
94     try {
95       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
96       cluster.waitActive();
97       String address = getRpcServerAddress(cluster);
98       assertThat("Bind address " + address + " is not wildcard.",
99                  address, is("/" + WILDCARD_ADDRESS));
100     } finally {
101       if (cluster != null) {
102         cluster.shutdown();
103       }
104     }
105   }
106 
107   @Test (timeout=300000)
testServiceRpcBindHostKey()108   public void testServiceRpcBindHostKey() throws IOException {
109     Configuration conf = new HdfsConfiguration();
110     MiniDFSCluster cluster = null;
111 
112     LOG.info("Testing without " + DFS_NAMENODE_SERVICE_RPC_BIND_HOST_KEY);
113 
114     conf.set(DFS_NAMENODE_SERVICE_RPC_ADDRESS_KEY, LOCALHOST_SERVER_ADDRESS);
115 
116     // NN should not bind the wildcard address by default.
117     try {
118       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
119       cluster.waitActive();
120       String address = getServiceRpcServerAddress(cluster);
121       assertThat("Bind address not expected to be wildcard by default.",
122                  address, not("/" + WILDCARD_ADDRESS));
123     } finally {
124       if (cluster != null) {
125         cluster.shutdown();
126         cluster = null;
127       }
128     }
129 
130     LOG.info("Testing with " + DFS_NAMENODE_SERVICE_RPC_BIND_HOST_KEY);
131 
132     // Tell NN to bind the wildcard address.
133     conf.set(DFS_NAMENODE_SERVICE_RPC_BIND_HOST_KEY, WILDCARD_ADDRESS);
134 
135     // Verify that NN binds wildcard address now.
136     try {
137       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
138       cluster.waitActive();
139       String address = getServiceRpcServerAddress(cluster);
140       assertThat("Bind address " + address + " is not wildcard.",
141                  address, is("/" + WILDCARD_ADDRESS));
142     } finally {
143       if (cluster != null) {
144         cluster.shutdown();
145       }
146     }
147   }
148 
149   @Test(timeout=300000)
testHttpBindHostKey()150   public void testHttpBindHostKey() throws IOException {
151     Configuration conf = new HdfsConfiguration();
152     MiniDFSCluster cluster = null;
153 
154     LOG.info("Testing without " + DFS_NAMENODE_HTTP_BIND_HOST_KEY);
155 
156     // NN should not bind the wildcard address by default.
157     try {
158       conf.set(DFS_NAMENODE_HTTP_ADDRESS_KEY, LOCALHOST_SERVER_ADDRESS);
159       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
160       cluster.waitActive();
161       String address = cluster.getNameNode().getHttpAddress().toString();
162       assertFalse("HTTP Bind address not expected to be wildcard by default.",
163                   address.startsWith(WILDCARD_ADDRESS));
164     } finally {
165       if (cluster != null) {
166         cluster.shutdown();
167         cluster = null;
168       }
169     }
170 
171     LOG.info("Testing with " + DFS_NAMENODE_HTTP_BIND_HOST_KEY);
172 
173     // Tell NN to bind the wildcard address.
174     conf.set(DFS_NAMENODE_HTTP_BIND_HOST_KEY, WILDCARD_ADDRESS);
175 
176     // Verify that NN binds wildcard address now.
177     try {
178       conf.set(DFS_NAMENODE_HTTP_ADDRESS_KEY, LOCALHOST_SERVER_ADDRESS);
179       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
180       cluster.waitActive();
181       String address = cluster.getNameNode().getHttpAddress().toString();
182       assertTrue("HTTP Bind address " + address + " is not wildcard.",
183                  address.startsWith(WILDCARD_ADDRESS));
184     } finally {
185       if (cluster != null) {
186         cluster.shutdown();
187       }
188     }
189   }
190 
191   private static final String BASEDIR = System.getProperty("test.build.dir",
192       "target/test-dir") + "/" + TestNameNodeRespectsBindHostKeys.class.getSimpleName();
193 
setupSsl()194   private static void setupSsl() throws Exception {
195     Configuration conf = new Configuration();
196     conf.setBoolean(DFSConfigKeys.DFS_WEBHDFS_ENABLED_KEY, true);
197     conf.set(DFSConfigKeys.DFS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name());
198     conf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0");
199     conf.set(DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, "localhost:0");
200 
201     File base = new File(BASEDIR);
202     FileUtil.fullyDelete(base);
203     assertTrue(base.mkdirs());
204     final String keystoresDir = new File(BASEDIR).getAbsolutePath();
205     final String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestNameNodeRespectsBindHostKeys.class);
206 
207     KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, conf, false);
208   }
209 
210   /**
211    * HTTPS test is different since we need to setup SSL configuration.
212    * NN also binds the wildcard address for HTTPS port by default so we must
213    * pick a different host/port combination.
214    * @throws Exception
215    */
216   @Test (timeout=300000)
testHttpsBindHostKey()217   public void testHttpsBindHostKey() throws Exception {
218     Configuration conf = new HdfsConfiguration();
219     MiniDFSCluster cluster = null;
220 
221     LOG.info("Testing behavior without " + DFS_NAMENODE_HTTPS_BIND_HOST_KEY);
222 
223     setupSsl();
224 
225     conf.set(DFS_HTTP_POLICY_KEY, HttpConfig.Policy.HTTPS_ONLY.name());
226 
227     // NN should not bind the wildcard address by default.
228     try {
229       conf.set(DFS_NAMENODE_HTTPS_ADDRESS_KEY, LOCALHOST_SERVER_ADDRESS);
230       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
231       cluster.waitActive();
232       String address = cluster.getNameNode().getHttpsAddress().toString();
233       assertFalse("HTTP Bind address not expected to be wildcard by default.",
234                   address.startsWith(WILDCARD_ADDRESS));
235     } finally {
236       if (cluster != null) {
237         cluster.shutdown();
238         cluster = null;
239       }
240     }
241 
242     LOG.info("Testing behavior with " + DFS_NAMENODE_HTTPS_BIND_HOST_KEY);
243 
244     // Tell NN to bind the wildcard address.
245     conf.set(DFS_NAMENODE_HTTPS_BIND_HOST_KEY, WILDCARD_ADDRESS);
246 
247     // Verify that NN binds wildcard address now.
248     try {
249       conf.set(DFS_NAMENODE_HTTPS_ADDRESS_KEY, LOCALHOST_SERVER_ADDRESS);
250       cluster = new MiniDFSCluster.Builder(conf).numDataNodes(0).build();
251       cluster.waitActive();
252       String address = cluster.getNameNode().getHttpsAddress().toString();
253       assertTrue("HTTP Bind address " + address + " is not wildcard.",
254                  address.startsWith(WILDCARD_ADDRESS));
255     } finally {
256       if (cluster != null) {
257         cluster.shutdown();
258       }
259     }
260   }
261 }
262