diff --git a/src/main/java/org/opensearch/security/tools/democonfig/Installer.java b/src/main/java/org/opensearch/security/tools/democonfig/Installer.java index 0b166ad580..a7b8531749 100644 --- a/src/main/java/org/opensearch/security/tools/democonfig/Installer.java +++ b/src/main/java/org/opensearch/security/tools/democonfig/Installer.java @@ -205,23 +205,28 @@ static void setOpenSearchVariables() { OPENSEARCH_LIB_PATH = BASE_DIR + "lib" + File.separator; OPENSEARCH_INSTALL_TYPE = determineInstallType(); + boolean shouldExit = false; if (!(new File(OPENSEARCH_CONF_FILE).exists())) { - System.out.println("Unable to determine OpenSearch config directory. Quit."); - System.exit(-1); + System.out.println("Unable to determine OpenSearch config file. Quit."); + shouldExit = true; } if (!(new File(OPENSEARCH_BIN_DIR).exists())) { System.out.println("Unable to determine OpenSearch bin directory. Quit."); - System.exit(-1); + shouldExit = true; } if (!(new File(OPENSEARCH_PLUGINS_DIR).exists())) { System.out.println("Unable to determine OpenSearch plugins directory. Quit."); - System.exit(-1); + shouldExit = true; } if (!(new File(OPENSEARCH_LIB_PATH).exists())) { System.out.println("Unable to determine OpenSearch lib directory. Quit."); + shouldExit = true; + } + + if (shouldExit) { System.exit(-1); } @@ -240,7 +245,8 @@ static String determineInstallType() { } // other OS (.sh execution) - if (new File("/usr/share/opensearch").equals(new File(BASE_DIR))) { + File opensearch = new File("/usr/share/opensearch"); + if (opensearch.exists() && opensearch.equals(new File(BASE_DIR))) { OPENSEARCH_CONF_FILE = "/usr/share/opensearch/config/opensearch.yml"; if (!new File(OPENSEARCH_CONF_FILE).exists()) { OPENSEARCH_CONF_FILE = "/etc/opensearch/opensearch.yml"; @@ -363,4 +369,26 @@ static void finishScriptExecution() { System.out.println(e.getMessage()); } } + + /** + * FOR TESTS ONLY + * Resets the class-level variables to their original values + * Is needed for tests since the variables are declared static + */ + static void resetState() { + assumeyes = false; + initsecurity = false; + cluster_mode = false; + environment = ExecutionEnvironment.DEMO; + SCRIPT_DIR = null; + BASE_DIR = null; + OPENSEARCH_CONF_FILE = null; + OPENSEARCH_BIN_DIR = null; + OPENSEARCH_PLUGINS_DIR = null; + OPENSEARCH_LIB_PATH = null; + OPENSEARCH_INSTALL_TYPE = null; + OPENSEARCH_CONF_DIR = null; + OPENSEARCH_VERSION = null; + SECURITY_VERSION = null; + } } diff --git a/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java b/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java index 1e318c38b9..8bf0161228 100644 --- a/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java +++ b/src/main/java/org/opensearch/security/tools/democonfig/SecuritySettingsConfigurer.java @@ -194,16 +194,16 @@ static String buildSecurityConfigString() { StringBuilder securityConfigLines = new StringBuilder(); securityConfigLines.append("\n") - .append("######## Start OpenSearch Security Demo Configuration ########\n") - .append("# WARNING: revise all the lines below before you go into production\n") - .append("plugins.security.ssl.transport.pemcert_filepath: esnode.pem\n") - .append("plugins.security.ssl.transport.pemkey_filepath: esnode-key.pem\n") - .append("plugins.security.ssl.transport.pemtrustedcas_filepath: root-ca.pem\n") + .append("######## Start OpenSearch Security Demo Configuration ########\n") + .append("# WARNING: revise all the lines below before you go into production\n") + .append("plugins.security.ssl.transport.pemcert_filepath: ").append(Certificates.NODE_CERT.getFileName()).append("\n") + .append("plugins.security.ssl.transport.pemkey_filepath: ").append(Certificates.NODE_KEY.getFileName()).append("\n") + .append("plugins.security.ssl.transport.pemtrustedcas_filepath: ").append(Certificates.ROOT_CA.getFileName()).append("\n") .append("plugins.security.ssl.transport.enforce_hostname_verification: false\n") .append("plugins.security.ssl.http.enabled: true\n") - .append("plugins.security.ssl.http.pemcert_filepath: esnode.pem\n") - .append("plugins.security.ssl.http.pemkey_filepath: esnode-key.pem\n") - .append("plugins.security.ssl.http.pemtrustedcas_filepath: root-ca.pem\n") + .append("plugins.security.ssl.http.pemcert_filepath: ").append(Certificates.NODE_CERT.getFileName()).append("\n") + .append("plugins.security.ssl.http.pemkey_filepath: ").append(Certificates.NODE_KEY.getFileName()).append("\n") + .append("plugins.security.ssl.http.pemtrustedcas_filepath: ").append(Certificates.ROOT_CA.getFileName()).append("\n") .append("plugins.security.allow_unsafe_democertificates: true\n"); if (initsecurity) { diff --git a/src/test/java/org/opensearch/security/tools/democonfig/InstallerTests.java b/src/test/java/org/opensearch/security/tools/democonfig/InstallerTests.java index 91e260434a..d056806d93 100644 --- a/src/test/java/org/opensearch/security/tools/democonfig/InstallerTests.java +++ b/src/test/java/org/opensearch/security/tools/democonfig/InstallerTests.java @@ -1,29 +1,40 @@ package org.opensearch.security.tools.democonfig; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; import java.io.PrintStream; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.opensearch.security.test.SingleClusterTest; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; public class InstallerTests extends SingleClusterTest { private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; + private final InputStream originalIn = System.in; - @BeforeEach + @Before public void setUpStreams() { System.setOut(new PrintStream(outContent)); + Installer.resetState(); } - @AfterEach + @After public void restoreStreams() { System.setOut(originalOut); + System.setIn(originalIn); } @Test @@ -31,44 +42,229 @@ public void testPrintScriptHeaders() { Installer.printScriptHeaders(); // Expected output - String expectedOutput = "**************************************************************************\n" - + "** This tool will be deprecated in the next major release of OpenSearch **\n" - + "** https://github.com/opensearch-project/security/issues/1755 **\n" - + "**************************************************************************\n" - + "\n\n" - + "### OpenSearch Security Demo Installer\n" - + "### ** Warning: Do not use on production or public reachable systems **\n"; - - assertEquals(expectedOutput, outContent.toString()); + String expectedOutput = "### OpenSearch Security Demo Installer\n" + + "### ** Warning: Do not use on production or public reachable systems **\n"; + + assertThat(expectedOutput, equalTo(outContent.toString())); } @Test public void testReadOptions() { // Test case 1: Valid options - String[] validOptions = { "scriptDir", "-y", "-i", "-c", "-s", "-t" }; + String[] validOptions = { "/scriptDir", "-y", "-i", "-c", "-s", "-t" }; Installer.readOptions(validOptions); - assertEquals("scriptDir", Installer.SCRIPT_DIR); - assertTrue(Installer.assumeyes); - assertTrue(Installer.initsecurity); - assertTrue(Installer.cluster_mode); + assertEquals("/scriptDir", Installer.SCRIPT_DIR); + assertThat(Installer.assumeyes, is(true)); + assertThat(Installer.initsecurity, is(true)); + assertThat(Installer.cluster_mode, is(true)); assertEquals(0, Installer.skip_updates); - assertEquals(ExecutionEnvironment.test, Installer.environment); - - // Test case 2: Help option - String[] helpOption = { "scriptDir", "-h" }; - Installer.readOptions(helpOption); - - assertTrue(outContent.toString().contains("install_demo_configuration.sh [-y] [-i] [-c]")); - assertTrue(outContent.toString().contains("-h show help")); - assertTrue(outContent.toString().contains("-y confirm all installation dialogues automatically")); - assertTrue(outContent.toString().contains("-i initialize Security plugin with default configuration")); - assertTrue(outContent.toString().contains("-c enable cluster mode by binding to all network interfaces")); - assertTrue(outContent.toString().contains("-s skip updates if config is already applied to opensearch.yml")); - assertTrue(outContent.toString().contains("-t set the execution environment to `test` to skip password validation")); - assertTrue(outContent.toString().contains("Should be used only for testing. (default is set to `demo`)")); - - // Reset System.out to restore normal behavior - System.setOut(System.out); + assertEquals(ExecutionEnvironment.TEST, Installer.environment); + + try { + System.setSecurityManager(new NoExitSecurityManager()); + // Test case 2: Help option + String[] helpOption = { "/scriptDir", "-h" }; + Installer.readOptions(helpOption); + + assertThat(outContent.toString(), containsString("install_demo_configuration.sh [-y] [-i] [-c]")); + assertThat(outContent.toString(), containsString("-h show help")); + assertThat(outContent.toString(), containsString("-y confirm all installation dialogues automatically")); + assertThat(outContent.toString(), containsString("-i initialize Security plugin with default configuration")); + assertThat(outContent.toString(), containsString("-c enable cluster mode by binding to all network interfaces")); + assertThat(outContent.toString(), containsString("-s skip updates if config is already applied to opensearch.yml")); + assertThat(outContent.toString(), containsString("-t set the execution environment to `test` to skip password validation")); + assertThat(outContent.toString(), containsString("Should be used only for testing. (default is set to `demo`)")); + } catch (SecurityException e) { + assertThat(e.getMessage(), equalTo("System.exit(0) blocked to allow print statement testing.")); + } finally { + System.setSecurityManager(null); + } + } + + @Test + public void testGatherUserInputsWithoutAssumeYes() { + // -i & -c option is not passed + String[] validOptions = { "/scriptDir" }; + Installer.readOptions(validOptions); + assertThat(Installer.assumeyes, is(false)); + assertThat(Installer.initsecurity, is(false)); + assertThat(Installer.cluster_mode, is(false)); + + // set initsecurity and cluster_mode to no + readInputStream("y\nn\nn\n"); // pass all 3 inputs as y + Installer.gatherUserInputs(); + + assertThat(outContent.toString(), containsString("Install demo certificates?")); + assertThat(outContent.toString(), containsString("Initialize Security Modules?")); + assertThat(outContent.toString(), containsString("Cluster mode requires additional setup of:")); + assertThat(outContent.toString(), containsString(" - Virtual memory (vm.max_map_count)\n")); + assertThat(outContent.toString(), containsString("Enable cluster mode?")); + + assertThat(Installer.initsecurity, is(false)); + assertThat(Installer.cluster_mode, is(false)); + + outContent.reset(); + + // set initsecurity and cluster_mode to no + readInputStream("y\ny\ny\n"); // pass all 3 inputs as y + Installer.gatherUserInputs(); + + assertThat(outContent.toString(), containsString("Install demo certificates?")); + assertThat(outContent.toString(), containsString("Initialize Security Modules?")); + assertThat(outContent.toString(), containsString("Cluster mode requires additional setup of:")); + assertThat(outContent.toString(), containsString(" - Virtual memory (vm.max_map_count)\n")); + assertThat(outContent.toString(), containsString("Enable cluster mode?")); + + assertThat(Installer.initsecurity, is(true)); + assertThat(Installer.cluster_mode, is(true)); + + outContent.reset(); + + // no to demo certificates + try { + System.setSecurityManager(new NoExitSecurityManager()); + + readInputStream("n\nn\nn\n"); + Installer.gatherUserInputs(); + + assertThat(outContent.toString(), containsString("Install demo certificates?")); + assertThat(outContent.toString(), not(containsString("Initialize Security Modules?"))); + assertThat(outContent.toString(), not(containsString("Cluster mode requires additional setup of:"))); + assertThat(outContent.toString(), not(containsString(" - Virtual memory (vm.max_map_count)\n"))); + assertThat(outContent.toString(), not(containsString("Enable cluster mode?"))); + } catch (SecurityException e) { + assertThat(e.getMessage(), equalTo("System.exit(0) blocked to allow print statement testing.")); + } finally { + System.setSecurityManager(null); + } + + outContent.reset(); + + // pass initsecurity and cluster_mode options + String[] validOptionsIC = { "/scriptDir", "-i", "-c" }; + Installer.readOptions(validOptionsIC); + assertThat(Installer.assumeyes, is(false)); + assertThat(Installer.initsecurity, is(true)); + assertThat(Installer.cluster_mode, is(true)); + + readInputStream("y\ny\ny\n"); // pass all 3 inputs as y + Installer.gatherUserInputs(); + + assertThat(outContent.toString(), containsString("Install demo certificates?")); + assertThat(outContent.toString(), not(containsString("Initialize Security Modules?"))); + assertThat(outContent.toString(), not(containsString("Enable cluster mode?"))); + + assertThat(Installer.initsecurity, is(true)); + assertThat(Installer.cluster_mode, is(true)); + } + + @Test + public void testGatherInputsWithAssumeYes() { + String[] validOptionsYes = { "/scriptDir", "-y" }; + Installer.readOptions(validOptionsYes); + assertThat(Installer.assumeyes, is(true)); + + Installer.gatherUserInputs(); + + assertThat(Installer.initsecurity, is(true)); + assertThat(Installer.cluster_mode, is(true)); + } + + @Test + public void testInitializeVariables_setBaseDir_invalidPath() { + String[] invalidScriptDirPath = { "/scriptDir", "-y" }; + Installer.readOptions(invalidScriptDirPath); + + assertThrows("Expected NullPointerException to be thrown", NullPointerException.class, Installer::initializeVariables); + + Installer.resetState(); + + String[] invalidScriptDirPath2 = { "/opensearch/plugins/opensearch-security/tools", "-y" }; + Installer.readOptions(invalidScriptDirPath2); + + try { + System.setSecurityManager(new NoExitSecurityManager()); + + Installer.initializeVariables(); + assertThat(outContent.toString(), containsString("DEBUG: basedir does not exist")); + } catch (SecurityException e) { + assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing.")); + } finally { + System.setSecurityManager(null); + } + } + + @Test + public void testSetBaseDir_valid() { + String currentDir = System.getProperty("user.dir"); + + String[] validBaseDir = { currentDir, "-y" }; + Installer.readOptions(validBaseDir); + + Installer.setBaseDir(); + + String expectedBaseDirValue = new File(currentDir).getParentFile().getParentFile().getParentFile().getAbsolutePath() + + File.separator; + assertThat(Installer.BASE_DIR, equalTo(expectedBaseDirValue)); + } + + @Test + public void testSetOpenSearchVariables_invalidPath() { + String currentDir = System.getProperty("user.dir"); + + String[] validBaseDir = { currentDir, "-y" }; + Installer.readOptions(validBaseDir); + + try { + System.setSecurityManager(new NoExitSecurityManager()); + + Installer.setBaseDir(); + Installer.setOpenSearchVariables(); + + assertThat(outContent.toString(), containsString("Unable to determine OpenSearch config file. Quit.")); + assertThat(outContent.toString(), containsString("Unable to determine OpenSearch bin directory. Quit.")); + assertThat(outContent.toString(), containsString("Unable to determine OpenSearch plugins directory. Quit.")); + assertThat(outContent.toString(), containsString("Unable to determine OpenSearch lib directory. Quit.")); + + } catch (SecurityException e) { + assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing.")); + } finally { + System.setSecurityManager(null); + } + + String expectedBaseDirValue = new File(currentDir).getParentFile().getParentFile().getParentFile().getAbsolutePath() + + File.separator; + String expectedOpensearchConfFilePath = expectedBaseDirValue + "config" + File.separator + "opensearch.yml"; + String expectedOpensearchBinDirPath = expectedBaseDirValue + "bin" + File.separator; + String expectedOpensearchPluginDirPath = expectedBaseDirValue + "plugins" + File.separator; + String expectedOpensearchLibDirPath = expectedBaseDirValue + "lib" + File.separator; + String expectedOpensearchInstallType = Installer.determineInstallType(); + + assertThat(Installer.OPENSEARCH_CONF_FILE, equalTo(expectedOpensearchConfFilePath)); + assertThat(Installer.OPENSEARCH_BIN_DIR, equalTo(expectedOpensearchBinDirPath)); + assertThat(Installer.OPENSEARCH_PLUGINS_DIR, equalTo(expectedOpensearchPluginDirPath)); + assertThat(Installer.OPENSEARCH_LIB_PATH, equalTo(expectedOpensearchLibDirPath)); + assertThat(Installer.OPENSEARCH_INSTALL_TYPE, equalTo(expectedOpensearchInstallType)); + + } + + private void readInputStream(String input) { + System.setIn(new ByteArrayInputStream(input.getBytes())); + } + +} + +class NoExitSecurityManager extends SecurityManager { + @Override + public void checkPermission(java.security.Permission perm) { + // Allow everything except System.exit code 0 &b -1 + if (perm instanceof java.lang.RuntimePermission && ("exitVM.0".equals(perm.getName()) || "exitVM.-1".equals(perm.getName()))) { + StringBuilder sb = new StringBuilder(); + sb.append("System.exit("); + sb.append(perm.getName().contains("0") ? 0 : -1); + sb.append(") blocked to allow print statement testing."); + throw new SecurityException(sb.toString()); + } } }