Skip to content

Commit

Permalink
Updates admin password string only if correct hash is present
Browse files Browse the repository at this point in the history
Signed-off-by: Darshit Chanpura <[email protected]>
  • Loading branch information
DarshitChanpura committed Mar 6, 2024
1 parent d235d97 commit d3b6441
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -97,7 +98,7 @@ public static Installer getInstance() {
* Installs the demo security configuration
* @param options the options passed to the script
*/
public void installDemoConfiguration(String[] options) {
public void installDemoConfiguration(String[] options) throws IOException {
readOptions(options);
printScriptHeaders();
gatherUserInputs();
Expand All @@ -108,7 +109,7 @@ public void installDemoConfiguration(String[] options) {
finishScriptExecution();
}

public static void main(String[] options) {
public static void main(String[] options) throws IOException {
Installer installer = Installer.getInstance();
installer.buildOptions();
installer.installDemoConfiguration(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public SecuritySettingsConfigurer(Installer installer) {
* 2. Sets the custom admin password (Generates one if none is provided)
* 3. Write the security config to opensearch.yml
*/
public void configureSecuritySettings() {
public void configureSecuritySettings() throws IOException {
checkIfSecurityPluginIsAlreadyConfigured();
updateAdminPassword();
writeSecurityConfigToOpenSearchYML();
Expand Down Expand Up @@ -125,9 +125,17 @@ void checkIfSecurityPluginIsAlreadyConfigured() {
/**
* Replaces the admin password in internal_users.yml with the custom or generated password
*/
void updateAdminPassword() {
void updateAdminPassword() throws IOException {
String INTERNAL_USERS_FILE_PATH = installer.OPENSEARCH_CONF_DIR + "opensearch-security" + File.separator + "internal_users.yml";
boolean shouldValidatePassword = installer.environment.equals(ExecutionEnvironment.DEMO);

// check if the password `admin` is present, if not skip updating admin password
if (!isAdminPasswordSetToAdmin(INTERNAL_USERS_FILE_PATH)) {
System.out.println("Admin password seems to be custom configured. Skipping update to admin password.");
return;
}

// if hashed value for "admin" password found, update it with the custom password
try {
final PasswordValidator passwordValidator = PasswordValidator.of(
Settings.builder()
Expand Down Expand Up @@ -180,6 +188,41 @@ void updateAdminPassword() {
}
}

/**
* Check if the password for admin user was already updated. (Possibly via a custom internal_users.yml)
* @param internalUsersFile Path to internal_users.yml file
* @return true if password was already updated, false otherwise
* @throws IOException if there was an error while reading the file
*/
private boolean isAdminPasswordSetToAdmin(String internalUsersFile) throws IOException {
boolean adminUserFound = false;
try (BufferedReader reader = new BufferedReader(new FileReader(internalUsersFile, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {

// After admin user is found, check first occurrence of hash (which will admin user's password)
// if hash is not for "admin" string, then skip updating password, else continue to update
if (line.matches("admin:")) {
adminUserFound = true;
continue;
}
// Once admin user is found, look for the first occurrence of a line starting with "hash:"
// Check that the line doesn't match the hash pattern for "admin" string
if (adminUserFound) {
if (line.matches(" *hash: *\"\\$2a\\$12\\$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG\"")) {
return true;
} else {
// Break, since we only need to find 'admin' user and validate their hash
break;
}
}
}
} catch (IOException e) {
throw new IOException("Exception while trying to read the internal users file to search for hashed `admin` password.");
}
return false;
}

/**
* Generate password hash and update it in the internal_users.yml file
* @param adminPassword the password to be hashed and updated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -66,13 +70,14 @@ public class SecuritySettingsConfigurerTests {
private static Installer installer;

@Before
public void setUp() {
public void setUp() throws IOException {
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(outContent));
installer = Installer.getInstance();
installer.buildOptions();
securitySettingsConfigurer = new SecuritySettingsConfigurer(installer);
setUpConf();
setUpInternalUsersYML();
}

@After
Expand All @@ -87,7 +92,7 @@ public void tearDown() throws NoSuchFieldException, IllegalAccessException {
}

@Test
public void testUpdateAdminPasswordWithCustomPassword() throws NoSuchFieldException, IllegalAccessException {
public void testUpdateAdminPasswordWithCustomPassword() throws NoSuchFieldException, IllegalAccessException, IOException {
String customPassword = "myStrongPassword123";
setEnv(adminPasswordKey, customPassword);

Expand All @@ -104,7 +109,7 @@ public void testUpdateAdminPassword_noPasswordSupplied() {
try {
System.setSecurityManager(new NoExitSecurityManager());
securitySettingsConfigurer.updateAdminPassword();
} catch (SecurityException e) {
} catch (SecurityException | IOException e) {
assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing."));
} finally {
System.setSecurityManager(null);
Expand All @@ -125,7 +130,7 @@ public void testUpdateAdminPasswordWithWeakPassword() throws NoSuchFieldExceptio
try {
System.setSecurityManager(new NoExitSecurityManager());
securitySettingsConfigurer.updateAdminPassword();
} catch (SecurityException e) {
} catch (SecurityException | IOException e) {
assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing."));
} finally {
System.setSecurityManager(null);
Expand All @@ -148,7 +153,7 @@ public void testUpdateAdminPasswordWithShortPassword() throws NoSuchFieldExcepti
try {
System.setSecurityManager(new NoExitSecurityManager());
securitySettingsConfigurer.updateAdminPassword();
} catch (SecurityException e) {
} catch (SecurityException | IOException e) {
assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing."));
} finally {
System.setSecurityManager(null);
Expand All @@ -160,7 +165,8 @@ public void testUpdateAdminPasswordWithShortPassword() throws NoSuchFieldExcepti
}

@Test
public void testUpdateAdminPasswordWithWeakPassword_skipPasswordValidation() throws NoSuchFieldException, IllegalAccessException {
public void testUpdateAdminPasswordWithWeakPassword_skipPasswordValidation() throws NoSuchFieldException, IllegalAccessException,
IOException {
setEnv(adminPasswordKey, "weakpassword");
installer.environment = ExecutionEnvironment.TEST;
securitySettingsConfigurer.updateAdminPassword();
Expand All @@ -170,6 +176,41 @@ public void testUpdateAdminPasswordWithWeakPassword_skipPasswordValidation() thr
verifyStdOutContainsString("Admin password set successfully.");
}

@Test
public void testUpdateAdminPasswordWithCustomInternalUsersYML() throws IOException {
String internalUsersFile = installer.OPENSEARCH_CONF_DIR + "opensearch-security" + File.separator + "internal_users.yml";
Path internalUsersFilePath = Paths.get(internalUsersFile);

List<String> newContent = Arrays.asList("admin:", " hash: \"$2b$12$totallyAHashString\"");
// overwriting existing content
Files.write(internalUsersFilePath, newContent, StandardCharsets.UTF_8);

securitySettingsConfigurer.updateAdminPassword();

verifyStdOutContainsString("Admin password seems to be custom configured. Skipping update to admin password.");
}

@Test
public void testUpdateAdminPasswordWithDefaultInternalUsersYml() throws IOException {

SecuritySettingsConfigurer.ADMIN_PASSWORD = ""; // to ensure 0 flaky-ness
try {
System.setSecurityManager(new NoExitSecurityManager());
securitySettingsConfigurer.updateAdminPassword();
} catch (SecurityException | IOException e) {
assertThat(e.getMessage(), equalTo("System.exit(-1) blocked to allow print statement testing."));
} finally {
System.setSecurityManager(null);
}

verifyStdOutContainsString(
String.format(
"No custom admin password found. Please provide a password via the environment variable %s.",
ConfigConstants.OPENSEARCH_INITIAL_ADMIN_PASSWORD
)
);
}

@Test
public void testSecurityPluginAlreadyConfigured() {
securitySettingsConfigurer.writeSecurityConfigToOpenSearchYML();
Expand Down Expand Up @@ -353,4 +394,11 @@ void setUpConf() {
private void verifyStdOutContainsString(String s) {
assertThat(outContent.toString(), containsString(s));
}

private void setUpInternalUsersYML() throws IOException {
String internalUsersFile = installer.OPENSEARCH_CONF_DIR + "opensearch-security" + File.separator + "internal_users.yml";
Path internalUsersFilePath = Paths.get(internalUsersFile);
List<String> defaultContent = Arrays.asList("admin:", " hash: \"$2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG\"");
Files.write(internalUsersFilePath, defaultContent, StandardCharsets.UTF_8);
}
}

0 comments on commit d3b6441

Please sign in to comment.