Browse Source

AMBARI-2626. Validate SSL port entered on ambari-server setup-https. (Oleksandr Diachenko via smohanty)

Sumit Mohanty 12 years ago
parent
commit
d28067bd3d

+ 16 - 0
ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java

@@ -57,6 +57,8 @@ public class Configuration {
   public static final String API_AUTHENTICATE = "api.authenticate";
   public static final String API_USE_SSL = "api.ssl";
   public static final String SRVR_TWO_WAY_SSL_KEY = "security.server.two_way_ssl";
+  public static final String SRVR_TWO_WAY_SSL_PORT_KEY = "security.server.two_way_ssl.port";
+  public static final String SRVR_ONE_WAY_SSL_PORT_KEY = "security.server.one_way_ssl.port";
   public static final String SRVR_KSTR_DIR_KEY = "security.server.keys_dir";
   public static final String SRVR_CRT_NAME_KEY = "security.server.cert_name";
   public static final String SRVR_KEY_NAME_KEY = "security.server.key_name";
@@ -176,6 +178,8 @@ public class Configuration {
 
 
   private static final String SRVR_TWO_WAY_SSL_DEFAULT = "false";
+  public static final String SRVR_TWO_WAY_SSL_PORT_DEFAULT = "8441";
+  public static final String SRVR_ONE_WAY_SSL_PORT_DEFAULT = "8440";
   private static final String SRVR_KSTR_DIR_DEFAULT = ".";
   public static final String SRVR_CRT_NAME_DEFAULT = "ca.crt";
   public static final String SRVR_KEY_NAME_DEFAULT = "ca.key";
@@ -258,6 +262,10 @@ public class Configuration {
     configsMap = new HashMap<String, String>();
     configsMap.put(SRVR_TWO_WAY_SSL_KEY, properties.getProperty(
         SRVR_TWO_WAY_SSL_KEY, SRVR_TWO_WAY_SSL_DEFAULT));
+    configsMap.put(SRVR_TWO_WAY_SSL_PORT_KEY, properties.getProperty(
+        SRVR_TWO_WAY_SSL_PORT_KEY, SRVR_TWO_WAY_SSL_PORT_DEFAULT));
+    configsMap.put(SRVR_ONE_WAY_SSL_PORT_KEY, properties.getProperty(
+        SRVR_ONE_WAY_SSL_PORT_KEY, SRVR_ONE_WAY_SSL_PORT_DEFAULT));
     configsMap.put(SRVR_KSTR_DIR_KEY, properties.getProperty(
         SRVR_KSTR_DIR_KEY, SRVR_KSTR_DIR_DEFAULT));
     configsMap.put(SRVR_CRT_NAME_KEY, properties.getProperty(
@@ -727,4 +735,12 @@ public class Configuration {
       properties.getProperty(SRVR_KSTR_DIR_KEY, SRVR_KSTR_DIR_DEFAULT));
     return defaultDir + File.separator + MASTER_KEY_FILENAME_DEFAULT;
   }
+
+  public int getOneWayAuthPort() {
+    return Integer.parseInt(properties.getProperty(SRVR_ONE_WAY_SSL_PORT_KEY, String.valueOf(SRVR_ONE_WAY_SSL_PORT_DEFAULT)));
+  }
+
+  public int getTwoWayAuthPort() {
+    return Integer.parseInt(properties.getProperty(SRVR_TWO_WAY_SSL_PORT_KEY, String.valueOf(SRVR_TWO_WAY_SSL_PORT_DEFAULT)));
+  }
 }

+ 3 - 5
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java

@@ -82,9 +82,6 @@ import com.sun.jersey.spi.container.servlet.ServletContainer;
 @Singleton
 public class AmbariServer {
   private static Logger LOG = LoggerFactory.getLogger(AmbariServer.class);
-  public static final int AGENT_ONE_WAY_AUTH = 8440;
-  public static final int AGENT_TWO_WAY_AUTH = 8441;
-
 
   private Server server = null;
   private Server serverForAgent = null;
@@ -196,7 +193,7 @@ public class AmbariServer {
       //Secured connector for 2-way auth
       SslSelectChannelConnector sslConnectorTwoWay = new
           SslSelectChannelConnector();
-      sslConnectorTwoWay.setPort(AGENT_TWO_WAY_AUTH);
+      sslConnectorTwoWay.setPort(configs.getTwoWayAuthPort());
 
       Map<String, String> configsMap = configs.getConfigsMap();
       String keystore = configsMap.get(Configuration.SRVR_KSTR_DIR_KEY) +
@@ -238,7 +235,7 @@ public class AmbariServer {
       // sslConnectorOneWay.setWantClientAuth(false);
       // sslConnectorOneWay.setNeedClientAuth(false);
       SslSelectChannelConnector sslConnectorOneWay = new SslSelectChannelConnector(contextFactory);
-      sslConnectorOneWay.setPort(AGENT_ONE_WAY_AUTH);
+      sslConnectorOneWay.setPort(configs.getOneWayAuthPort());
       sslConnectorOneWay.setAcceptors(2);
       sslConnectorTwoWay.setAcceptors(2);
       serverForAgent.setConnectors(new Connector[]{ sslConnectorOneWay, sslConnectorTwoWay});
@@ -436,6 +433,7 @@ public class AmbariServer {
     StageUtils.setGson(injector.getInstance(Gson.class));
     WorkflowJsonService.setDBProperties(
         injector.getInstance(Configuration.class));
+    SecurityFilter.init(injector.getInstance(Configuration.class));
   }
 
   public static void main(String[] args) throws Exception {

+ 0 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java

@@ -24,7 +24,6 @@ import com.google.inject.Scopes;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.persist.jpa.JpaPersistModule;
 import org.apache.ambari.server.actionmanager.*;
-import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.orm.PersistenceType;
 import org.apache.ambari.server.serveraction.ServerActionManager;

+ 9 - 3
ambari-server/src/main/java/org/apache/ambari/server/security/SecurityFilter.java

@@ -30,14 +30,16 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.ambari.server.controller.AmbariServer;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 public class SecurityFilter implements Filter {
-	
+  
   //Allowed pathes for one way auth https
   private static String CA = "/ca";
+
+  private static Configuration config;
   private final static Log LOG = LogFactory.getLog(SecurityFilter.class);
 
   @Override
@@ -52,7 +54,7 @@ public class SecurityFilter implements Filter {
     String reqUrl = req.getRequestURL().toString();
 
     LOG.debug("Filtering " + reqUrl + " for security purposes");
-    if (serReq.getLocalPort() != AmbariServer.AGENT_TWO_WAY_AUTH) {
+    if (serReq.getLocalPort() != config.getTwoWayAuthPort()) {
       if (isRequestAllowed(reqUrl)) {
         filtCh.doFilter(serReq, serResp);
       }
@@ -97,4 +99,8 @@ public class SecurityFilter implements Filter {
     LOG.warn("Request " + reqUrl + " doesn't match any pattern.");
     return false;
   }
+
+  public static void init(Configuration instance) {
+    config = instance;
+  }
 }

+ 42 - 4
ambari-server/src/main/python/ambari-server.py

@@ -232,6 +232,12 @@ JDBC_RCA_PASSWORD_FILENAME = "rca_password.dat"
 CLIENT_API_PORT_PROPERTY = "client.api.port"
 CLIENT_API_PORT = "8080"
 
+SRVR_TWO_WAY_SSL_PORT_PROPERTY = "security.server.two_way_ssl.port"
+SRVR_TWO_WAY_SSL_PORT = "8441"
+
+SRVR_ONE_WAY_SSL_PORT_PROPERTY = "security.server.one_way_ssl.port"
+SRVR_ONE_WAY_SSL_PORT = "8440"
+
 PERSISTENCE_TYPE_PROPERTY = "server.persistence.type"
 JDBC_DRIVER_PROPERTY = "server.jdbc.driver"
 JDBC_URL_PROPERTY = "server.jdbc.url"
@@ -2332,7 +2338,8 @@ def get_choice_string_input(prompt,default,firstChoice,secondChoice):
 
 
 def get_validated_string_input(prompt, default, pattern, description,
-                               is_pass, allowEmpty=True):
+                               is_pass, allowEmpty=True, validatorFunction=None):
+
   input = ""
   while not input:
     if SILENT:
@@ -2350,12 +2357,20 @@ def get_validated_string_input(prompt, default, pattern, description,
         continue
       else:
         input = default
+        if validatorFunction:
+          if not validatorFunction(input):
+            input = ""
+            continue
         break #done here and picking up default
     else:
       if not pattern==None and not re.search(pattern,input.strip()):
         print description
         input= ""
-
+        
+      if validatorFunction:
+        if not validatorFunction(input):
+          input = ""
+          continue
   return input
 
 
@@ -2965,13 +2980,13 @@ def setup_https(args):
                                 get_validated_string_input(\
                                 "SSL port ["+str(client_api_ssl_port)+"] ? ",\
                                 str(client_api_ssl_port),\
-                                "^[0-9]{1,5}$", "Invalid port.", False))   
+                                "^[0-9]{1,5}$", "Invalid port.", False, validatorFunction = is_valid_https_port))
         cert_was_imported = import_cert_and_key_action(security_server_keys_dir, properties)
       else:
        if get_YN_input("Do you want to configure HTTPS [y/n] (y)? ", True):
         properties.process_pair(SSL_API_PORT,\
         get_validated_string_input("SSL port ["+str(client_api_ssl_port)+"] ? ",\
-        str(client_api_ssl_port), "^[0-9]{1,5}$", "Invalid port.", False))   
+        str(client_api_ssl_port), "^[0-9]{1,5}$", "Invalid port.", False, validatorFunction = is_valid_https_port))
         cert_was_imported = import_cert_and_key_action(security_server_keys_dir, properties)
        else:
         return
@@ -3217,7 +3232,30 @@ def is_valid_cert_host(certInfoDict):
     return False
 
   return True
+  
+def is_valid_https_port(port):
+  properties = get_ambari_properties()
+  if properties == -1:
+    print "Error getting ambari properties"
+    return False
+
+  one_way_port = properties[SRVR_ONE_WAY_SSL_PORT_PROPERTY]
+  if not one_way_port:
+    one_way_port = SRVR_ONE_WAY_SSL_PORT
+
+  two_way_port = properties[SRVR_TWO_WAY_SSL_PORT_PROPERTY]
+  if not two_way_port:
+    two_way_port = SRVR_TWO_WAY_SSL_PORT
+
+  if port.strip() == one_way_port.strip():
+    print "Port for https can't match the port for one way authentication port(" + one_way_port + ")"
+    return False
+
+  if port.strip() == two_way_port.strip():
+    print "Port for https can't match the port for two way authentication port(" + two_way_port + ")"
+    return False
 
+  return True
 
 def get_fqdn():
   properties = get_ambari_properties()

+ 10 - 0
ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java

@@ -25,6 +25,7 @@ import junit.framework.Assert;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.RandomStringUtils;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -114,6 +115,13 @@ public class ConfigurationTest {
         Configuration.CLIENT_API_SSL_CRT_PASS_FILE_NAME_KEY,
         passFile.getName());
     
+    
+    String oneWayPort = RandomStringUtils.randomNumeric(4);
+    String twoWayPort = RandomStringUtils.randomNumeric(4);
+    
+    ambariProperties.setProperty(Configuration.SRVR_TWO_WAY_SSL_PORT_KEY, twoWayPort.toString());
+    ambariProperties.setProperty(Configuration.SRVR_ONE_WAY_SSL_PORT_KEY, oneWayPort.toString());
+    
     Configuration conf = new Configuration(ambariProperties);
     Assert.assertTrue(conf.getApiSSLAuthentication());
 
@@ -128,6 +136,8 @@ public class ConfigurationTest {
     Assert.assertEquals(passFile.getName(), conf.getConfigsMap().get(
       Configuration.CLIENT_API_SSL_CRT_PASS_FILE_NAME_KEY));
     Assert.assertEquals(password, conf.getConfigsMap().get(Configuration.CLIENT_API_SSL_CRT_PASS_KEY));
+    Assert.assertEquals(Integer.parseInt(twoWayPort), conf.getTwoWayAuthPort());
+    Assert.assertEquals(Integer.parseInt(oneWayPort), conf.getOneWayAuthPort());
 
   }
 

+ 19 - 0
ambari-server/src/test/java/org/apache/ambari/server/security/SecurityFilterTest.java

@@ -18,6 +18,13 @@
 
 package org.apache.ambari.server.security;
 
+import java.io.IOException;
+
+
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.junit.Before;
 import org.junit.Test;
 import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
@@ -26,7 +33,19 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
 
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
 public class SecurityFilterTest {
+  
+  private Injector injector;
+
+  @Before
+  public void setUp() throws IOException {
+    injector = Guice.createInjector(new InMemoryDefaultTestModule());
+    SecurityFilter.init(injector.getInstance(Configuration.class));
+  }
+  
   @Test
   public void mustFilterNonHttpsRequests() throws Exception {
     SecurityFilter filter = new SecurityFilter();

+ 52 - 2
ambari-server/src/test/python/TestAmbaryServer.py

@@ -1242,7 +1242,7 @@ class TestAmbariServer(TestCase):
     self.assertTrue(is_valid)
     
   @patch.object(ambari_server, "get_fqdn")
-  def is_valid_cert_host(self, get_fqdn_mock):
+  def test_is_valid_cert_host(self, get_fqdn_mock):
     
     #No data in certInfo
     certInfo = {}
@@ -1264,7 +1264,35 @@ class TestAmbariServer(TestCase):
     get_fqdn_mock.return_value = 'host1'
     certInfo = {ambari_server.COMMON_NAME_ATTR : 'host1'}
     is_valid = ambari_server.is_valid_cert_host(certInfo)
-    self.assertFalse(is_valid)
+    self.assertTrue(is_valid)
+    
+
+  @patch.object(ambari_server, "get_ambari_properties")
+  def test_is_valid_https_port(self, get_ambari_properties_mock):
+    
+    #No ambari.properties
+    get_ambari_properties_mock.return_value = -1
+    is_valid = ambari_server.is_valid_https_port(1111)
+    self.assertEqual(is_valid, False)
+    
+    #User entered port used by one way auth
+    portOneWay = "1111"
+    portTwoWay = "2222"
+    validPort = "3333"
+    get_ambari_properties_mock.return_value = {ambari_server.SRVR_ONE_WAY_SSL_PORT_PROPERTY : portOneWay,
+                                               ambari_server.SRVR_TWO_WAY_SSL_PORT_PROPERTY : portTwoWay}
+    is_valid = ambari_server.is_valid_https_port(portOneWay)
+    self.assertEqual(is_valid, False)
+    
+    #User entered port used by two way auth
+    is_valid = ambari_server.is_valid_https_port(portTwoWay)
+    self.assertEqual(is_valid, False)
+    
+    #User entered valid port
+    get_ambari_properties_mock.return_value = {ambari_server.SRVR_ONE_WAY_SSL_PORT_PROPERTY : portOneWay,
+                                               ambari_server.SRVR_TWO_WAY_SSL_PORT_PROPERTY : portTwoWay}
+    is_valid = ambari_server.is_valid_https_port(validPort)
+    self.assertEqual(is_valid, True)
 
   @patch("socket.getfqdn")
   @patch("urllib2.urlopen")
@@ -1348,6 +1376,28 @@ MIIFHjCCAwYCCQDpHKOBI+Lt0zANBgkqhkiG9w0BAQUFADBRMQswCQYDVQQGEwJV
     self.assertEqual(cert_info[attr3_key], attr3_value)
 
       
+  @patch('__builtin__.raw_input')
+  def test_get_validated_string_input(self, raw_input_mock):
+    prompt = 'prompt'
+    default_value = 'default'
+    description = 'desc'
+    validator = MagicMock()
+    validator.return_value = True
+    inputed_value1 = 'val1'
+    inputed_value2 = 'val2'
+    raw_input_mock.return_value = inputed_value1
+    input = ambari_server.get_validated_string_input(prompt, default_value, None,
+                                             description, False, False, validator)
+    self.assertTrue(validator.called)
+    self.assertEqual(inputed_value1, input)
+    
+    validator.side_effect = [False, True]
+    raw_input_mock.side_effect = [inputed_value1, inputed_value2]
+    input = ambari_server.get_validated_string_input(prompt, default_value, None,
+                                             description, False, False, validator)
+    self.assertEqual(inputed_value2, input)
+    
+    
 
   @patch.object(ambari_server, "run_os_command")
   @patch("__builtin__.open")