Closed AlexRuiz7 closed 1 month ago
We found that OpenSearch's Observability Plugin uses a dedicated configuration file, observability.yml. We're going to research how they use this configuration file so we can use the same approach.
We found that they use this method from the Setting class that loads the configuration file
We searched other uses of the Setting
class and found this use in the Security Plugin which is implemented in Java.
The AuditConfigMigrater class in Security Plugin reads the opensearch.yml, then creates new configuration files and finally edits the opensearch.yml. So, we understand that this is a complete example to use opensearch.yml to persist configuration options for the plugin.
final CommandLine line = parser.parse(options, args);
[...]
final String source = line.getOptionValue("s", opensearchPath);
[...]
// create settings builder
System.out.println("Using source opensearch.yml file from path " + source);
final Settings.Builder settingsBuilder = Settings.builder().loadFromPath(Paths.get(source));
We investigated the java.nio.file.Paths
class to understand if that uses absolute or relative path. We can use relatives and absolutes paths:
Path path = Paths.get("/home/mcasas/myfile.txt");
Path path = Paths.get("/home/mcasas/myfile.txt");
Path absPath = p.toAbsolutePath();
Path path = Paths.get("mcasas/myfile.txt");
Path currentDir = Paths.get(".");
As a conclusion, any configuration file can be loaded on runtime using the Settings class builder. The PluginSettings class from the Observability plugin is a great example of how to define, load and validate plugin specific settings.
Settings.builder().loadFromPath(defaultSettingYmlFile)
This method can be used for reading any configuration in the file system.
While taking a look at the Settings class, I noticed the KeyStoreWrapper class, which presumably can be used to load stuff from the keystore.
We should take a look at it.
This class allow us to load an opensearch.keystore file, read the keys and also write new keys.
The algorithm used to derive the cipher key from a password is "PBKDF2WithHmacSHA512". The cipher used to encrypt the keystore data is "AES".
try (KeyStoreWrapper keystore = KeyStoreWrapper.load(environment.configDir())) {
// reread keystore from config file
if (keystore == null) {
return new NodesReloadSecureSettingsResponse.NodeResponse(
clusterService.localNode(),
new IllegalStateException("Keystore is missing")
);
}
// decrypt the keystore using the password from the request
keystore.decrypt(secureSettingsPassword.getChars());
// add the keystore to the original node settings object
final Settings settingsWithKeystore = Settings.builder().put(environment.settings(), false).setSecureSettings(keystore).build();
final List<Exception> exceptions = new ArrayList<>();
// broadcast the new settings object (with the open embedded keystore) to all reloadable plugins
pluginsService.filterPlugins(ReloadablePlugin.class).stream().forEach(p -> {
try {
p.reload(settingsWithKeystore);
} catch (final Exception e) {
logger.warn(
(Supplier<?>) () -> new ParameterizedMessage("Reload failed for plugin [{}]", p.getClass().getSimpleName()),
e
);
exceptions.add(e);
}
});
static SecureSettings loadSecureSettings(Environment initialEnv) throws BootstrapException {
final KeyStoreWrapper keystore;
try {
keystore = KeyStoreWrapper.load(initialEnv.configDir());
} catch (IOException e) {
throw new BootstrapException(e);
}
SecureString password;
try {
if (keystore != null && keystore.hasPassword()) {
password = readPassphrase(System.in, KeyStoreAwareCommand.MAX_PASSPHRASE_LENGTH);
} else {
password = new SecureString(new char[0]);
}
} catch (IOException e) {
throw new BootstrapException(e);
}
try {
if (keystore == null) {
final KeyStoreWrapper keyStoreWrapper = KeyStoreWrapper.create();
keyStoreWrapper.save(initialEnv.configDir(), new char[0]);
return keyStoreWrapper;
} else {
keystore.decrypt(password.getChars());
KeyStoreWrapper.upgrade(keystore, initialEnv.configDir(), password.getChars());
}
} catch (Exception e) {
throw new BootstrapException(e);
} finally {
password.close();
}
return keystore;
}
More uses of the KeyStoreWrapper
class here.
We have all the information we need for the next step, which is to implement one of these configuration persistence methods in our command-manager
plugin. Given our use case, which is to store the URL and credentials of the Management API on the Wazuh Server, I think the key store method fits better, as that's sensitive information.
Description
As part of the development of the Command Manager plugin, we need to investigate which of the methods to persist configuration options for the plugin suits our needs best.
opensearch.yml
)./usr/share/wazuh-indexer/bin/opensearch-keystore
&/etc/wazuh-indexer/opensearch.keystore
).The goal of this issue is to: