AMBARI-2626. Validate SSL port entered on ambari-server setup-https. (Oleksandr Diachenko via smohanty)
Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/d28067bd Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/d28067bd Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/d28067bd Branch: refs/heads/trunk Commit: d28067bd3dc8288f3153a3602321793301279dcd Parents: dfd4b1a Author: Sumit Mohanty <[email protected]> Authored: Thu Jul 11 20:48:01 2013 -0700 Committer: Sumit Mohanty <[email protected]> Committed: Thu Jul 11 20:48:01 2013 -0700 ---------------------------------------------------------------------- .../server/configuration/Configuration.java | 16 ++++++ .../ambari/server/controller/AmbariServer.java | 8 ++- .../server/controller/ControllerModule.java | 1 - .../ambari/server/security/SecurityFilter.java | 12 +++-- ambari-server/src/main/python/ambari-server.py | 46 +++++++++++++++-- .../server/configuration/ConfigurationTest.java | 10 ++++ .../server/security/SecurityFilterTest.java | 19 +++++++ .../src/test/python/TestAmbaryServer.py | 54 +++++++++++++++++++- 8 files changed, 151 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java index 42b603d..0109ae8 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java +++ b/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))); + } } http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index 0ade157..a04ab61 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/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 { http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java index 2e16034..261b2a3 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java +++ b/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; http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/main/java/org/apache/ambari/server/security/SecurityFilter.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/SecurityFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/security/SecurityFilter.java index 94b43e1..9d4e603 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/SecurityFilter.java +++ b/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; + } } http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/main/python/ambari-server.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py index 06cc86c..5ffbcf7 100755 --- a/ambari-server/src/main/python/ambari-server.py +++ b/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() http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java b/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java index d6367a5..48674e4 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/configuration/ConfigurationTest.java +++ b/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()); } http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityFilterTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityFilterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityFilterTest.java index 647636f..49eefed 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/security/SecurityFilterTest.java +++ b/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(); http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/d28067bd/ambari-server/src/test/python/TestAmbaryServer.py ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/python/TestAmbaryServer.py b/ambari-server/src/test/python/TestAmbaryServer.py index 7198ace..330fc9b 100644 --- a/ambari-server/src/test/python/TestAmbaryServer.py +++ b/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")
