Refactored all usages of directories used by datanode and managed opensearch (#16905)

* Refactored all usages of directories used by datanode and managed opensearch

* add datanode directories preflight check

* code cleanup

* fixed OpensearchSecurityConfiguration

* Fixed DatanodeDirectories logging

* code cleanup
This commit is contained in:
Tomas Dvorak
2023-10-13 08:16:58 +02:00
committed by GitHub
parent d575869444
commit bede9d3206
23 changed files with 458 additions and 80 deletions

View File

@@ -30,7 +30,7 @@ import com.github.joschi.jadconfig.validators.URIAbsoluteValidator;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.net.InetAddresses;
import org.graylog.datanode.configuration.BaseConfiguration;
import org.graylog.datanode.configuration.OpensearchDistributionProvider;
import org.graylog.datanode.configuration.DatanodeDirectories;
import org.graylog2.plugin.Tools;
import org.graylog2.shared.SuppressForbidden;
import org.joda.time.DateTimeZone;
@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.URI;
@@ -83,16 +82,16 @@ public class Configuration extends BaseConfiguration {
@Parameter(value = "opensearch_location")
private String opensearchDistributionRoot = "dist";
@Parameter(value = "opensearch_data_location")
@Parameter(value = "opensearch_data_location", required = true)
private String opensearchDataLocation = "datanode/data";
@Parameter(value = "opensearch_logs_location")
@Parameter(value = "opensearch_logs_location", required = true)
private String opensearchLogsLocation = "datanode/logs";
@Parameter(value = "opensearch_config_location")
@Parameter(value = "opensearch_config_location", required = true)
private String opensearchConfigLocation = "datanode/config";
@Parameter(value = "config_location")
@Parameter(value = "config_location", required = true)
private String configLocation = "config";
@Parameter(value = "process_logs_buffer_size")
@@ -235,18 +234,33 @@ public class Configuration extends BaseConfiguration {
return opensearchDistributionRoot;
}
/**
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
*/
public String getOpensearchConfigLocation() {
return opensearchConfigLocation;
}
public String getConfigLocation() {
/**
* This is a pointer to a directory holding configuration files (and certificates) for the datanode itself.
* We treat it as read only for the datanode and should never persist anything in it.
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
*/
public String getDatanodeConfigurationLocation() {
return configLocation;
}
/**
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
*/
public String getOpensearchDataLocation() {
return opensearchDataLocation;
}
/**
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
*/
public String getOpensearchLogsLocation() {
return opensearchLogsLocation;
}

View File

@@ -20,6 +20,7 @@ import org.graylog.datanode.configuration.OpensearchArchitecture;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.nio.file.Paths;
public record OpensearchDistribution(Path directory, String version, @Nullable String platform,
@Nullable OpensearchArchitecture architecture) {
@@ -28,4 +29,7 @@ public record OpensearchDistribution(Path directory, String version, @Nullable S
this(path, version, null, null);
}
public Path getOpensearchExecutable() {
return directory.resolve(Paths.get("bin", "opensearch"));
}
}

View File

@@ -17,14 +17,17 @@
package org.graylog.datanode.bindings;
import com.google.inject.AbstractModule;
import org.graylog.datanode.OpensearchDistribution;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.datanode.configuration.DatanodeConfigurationProvider;
import org.graylog2.plugin.system.FilePersistedNodeIdProvider;
import org.graylog2.plugin.system.NodeId;
import org.graylog.datanode.configuration.OpensearchDistributionProvider;
import org.graylog.datanode.OpensearchDistribution;
public class DatanodeConfigurationBindings extends AbstractModule {
@Override
protected void configure() {
bind(NodeId.class).toProvider(FilePersistedNodeIdProvider.class).asEagerSingleton();
bind(DatanodeConfiguration.class).toProvider(DatanodeConfigurationProvider.class);
bind(OpensearchDistribution.class).toProvider(OpensearchDistributionProvider.class);
}

View File

@@ -18,6 +18,7 @@ package org.graylog.datanode.bindings;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.MapBinder;
import org.graylog.datanode.bootstrap.preflight.DatanodeDirectoriesCheck;
import org.graylog.datanode.bootstrap.preflight.OpensearchBinPreflightCheck;
import org.graylog.datanode.bootstrap.preflight.OpensearchConfigSync;
import org.graylog2.bootstrap.preflight.PreflightCheck;
@@ -29,6 +30,7 @@ public class PreflightChecksBindings extends AbstractModule {
protected void configure() {
addPreflightCheck(OpensearchConfigSync.class);
addPreflightCheck(OpensearchBinPreflightCheck.class);
addPreflightCheck(DatanodeDirectoriesCheck.class);
}

View File

@@ -68,7 +68,6 @@ public class ServerBindings extends Graylog2Module {
private void bindProviders() {
bind(ClusterEventBus.class).toProvider(ClusterEventBusProvider.class).asEagerSingleton();
bind(EventBus.class).toProvider(EventBusProvider.class).asEagerSingleton();
bind(NodeId.class).toProvider(FilePersistedNodeIdProvider.class).asEagerSingleton();
bind(InputConfigurationBeanDeserializerModifier.class).toInstance(InputConfigurationBeanDeserializerModifier.withoutConfig());
}

View File

@@ -17,7 +17,7 @@
package org.graylog.datanode.bootstrap.preflight;
import org.bouncycastle.operator.OperatorException;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.security.certutil.CertConstants;
import org.graylog.security.certutil.cert.CertificateChain;
import org.graylog.security.certutil.cert.storage.CertChainMongoStorage;
@@ -43,7 +43,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import java.nio.file.Path;
import java.security.KeyStore;
import java.util.Optional;
@@ -72,7 +71,7 @@ public class DataNodeConfigurationPeriodical extends Periodical {
final CertificateAndPrivateKeyMerger certificateAndPrivateKeyMerger,
final SmartKeystoreStorage keystoreStorage,
final @Named("password_secret") String passwordSecret,
final Configuration configuration) {
final DatanodeConfiguration datanodeConfiguration) {
this.dataNodeProvisioningService = dataNodeProvisioningService;
this.nodeService = nodeService;
this.nodeId = nodeId;
@@ -82,7 +81,7 @@ public class DataNodeConfigurationPeriodical extends Periodical {
this.certificateAndPrivateKeyMerger = certificateAndPrivateKeyMerger;
this.keystoreStorage = keystoreStorage;
// TODO: merge with real storage
this.privateKeyEncryptedStorage = new PrivateKeyEncryptedFileStorage(Path.of(configuration.getOpensearchConfigLocation()).resolve("privateKey.cert"));
this.privateKeyEncryptedStorage = new PrivateKeyEncryptedFileStorage(datanodeConfiguration.datanodeDirectories().getConfigurationTargetDir().resolve("privateKey.cert"));
this.passwordSecret = passwordSecret.toCharArray();
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog.datanode.bootstrap.preflight;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.datanode.configuration.DatanodeDirectories;
import org.graylog2.bootstrap.preflight.PreflightCheck;
import org.graylog2.bootstrap.preflight.PreflightCheckException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* This check will verify that all pointers to all directories that the datanode and its managed opensearch process
* are available and configured with correct permissions.
*
* Checking this early will prevent some later hard to understand exceptions, for example errors when starting the opensearch
* process with non-writeable configuration directory.
*/
public class DatanodeDirectoriesCheck implements PreflightCheck {
private static final Logger LOG = LoggerFactory.getLogger(DatanodeDirectoriesCheck.class);
private final DatanodeConfiguration datanodeConfiguration;
@Inject
public DatanodeDirectoriesCheck(DatanodeConfiguration datanodeConfiguration) {
this.datanodeConfiguration = datanodeConfiguration;
}
@Override
public void runCheck() throws PreflightCheckException {
try {
checkDirectories(datanodeConfiguration.datanodeDirectories());
} catch (DatanodeDirectoryException e) {
throw new PreflightCheckException("Failed to check required datanode directories", e);
}
}
private void checkDirectories(DatanodeDirectories directories) throws DatanodeDirectoryException {
checkReadWriteDir(directories.getDataTargetDir(), "opensearch_data_location");
checkReadWriteDir(directories.getLogsTargetDir(), "opensearch_logs_location");
checkReadWriteDir(directories.getOpensearchProcessConfigurationDir(), "opensearch_config_location");
checkReadDir(directories.getConfigurationSourceDir(), "config_location");
}
static void checkReadDir(Path dir, String configPropertyName) throws DatanodeDirectoryException {
checkOrCreate(dir, configPropertyName);
checkReadable(dir, configPropertyName);
}
static void checkReadWriteDir(Path dir, String configPropertyName) throws DatanodeDirectoryException {
checkOrCreate(dir, configPropertyName);
checkReadable(dir, configPropertyName);
checkWriteable(dir, configPropertyName);
}
private static void checkOrCreate(Path dir, String configPropertyName) throws DatanodeDirectoryException {
if (!Files.exists(dir)) {
createDirectory(dir, configPropertyName);
} else if (!Files.isDirectory(dir)) {
throw new DatanodeDirectoryException("Datanode expects " + dir + " to be a directory, please make sure the configuration property " + configPropertyName + " points to a directory.");
}
}
private static void createDirectory(Path dir, String configPropertyName) throws DatanodeDirectoryException {
try {
LOG.info("Datanode directory " + dir + " doesn't existing, creating now.");
Files.createDirectories(dir);
} catch (IOException e) {
throw new DatanodeDirectoryException("Failed to create directory " + dir + ". Please make sure that user running datanode has permissions to create directory there or adapt configuration property " + configPropertyName, e);
}
}
private static void checkReadable(Path dir, String configPropertyName) throws DatanodeDirectoryException {
if (!Files.isReadable(dir)) {
throw new DatanodeDirectoryException("Datanode needs READ permissions to the " + dir + " directory. Please configure READ permissions for the user that runs the datanode or change the " + configPropertyName + " configuration");
}
}
private static void checkWriteable(Path dir, String configPropertyName) throws DatanodeDirectoryException {
if (!Files.isWritable(dir)) {
throw new DatanodeDirectoryException("Datanode needs WRITE permissions to the " + dir + " directory. Please configure WRITE permissions for the user that runs the datanode or change the " + configPropertyName + " configuration.");
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog.datanode.bootstrap.preflight;
import java.io.IOException;
public class DatanodeDirectoryException extends IOException {
public DatanodeDirectoryException(String message) {
super(message);
}
public DatanodeDirectoryException(String message, IOException cause) {
super(message, cause);
}
}

View File

@@ -16,6 +16,7 @@
*/
package org.graylog.datanode.bootstrap.preflight;
import org.graylog.datanode.OpensearchDistribution;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog2.bootstrap.preflight.PreflightCheck;
import org.graylog2.bootstrap.preflight.PreflightCheckException;
@@ -26,34 +27,35 @@ import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class OpensearchBinPreflightCheck implements PreflightCheck {
private final Supplier<Path> opensearchDirSupplier;
private final Supplier<OpensearchDistribution> opensearchDistributionSupplier;
private static final Logger LOG = LoggerFactory.getLogger(OpensearchBinPreflightCheck.class);
@Inject
public OpensearchBinPreflightCheck(DatanodeConfiguration datanodeConfiguration) {
this(() -> datanodeConfiguration.opensearchDistributionProvider().get().directory());
this(datanodeConfiguration.opensearchDistributionProvider()::get);
}
public OpensearchBinPreflightCheck(Supplier<Path> opensearchBaseDirectory) {
this.opensearchDirSupplier = opensearchBaseDirectory;
OpensearchBinPreflightCheck(Supplier<OpensearchDistribution> distribution) {
this.opensearchDistributionSupplier = distribution;
}
@Override
public void runCheck() throws PreflightCheckException {
final Path opensearchDir = opensearchDirSupplier.get();
final OpensearchDistribution distribution = opensearchDistributionSupplier.get();
final Path opensearchDir = distribution.directory();
if (!Files.isDirectory(opensearchDir)) {
throw new PreflightCheckException("Opensearch base directory " + opensearchDir + " doesn't exist!");
}
final Path binPath = opensearchDir.resolve(Paths.get("bin", "opensearch"));
final Path binPath = distribution.getOpensearchExecutable();
if (!Files.exists(binPath)) {
throw new PreflightCheckException("Opensearch binary " + binPath + " doesn't exist!");

View File

@@ -16,9 +16,7 @@
*/
package org.graylog.datanode.bootstrap.preflight;
import org.apache.logging.log4j.core.appender.rolling.action.DeletingVisitor;
import org.apache.logging.log4j.core.appender.rolling.action.PathCondition;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog2.bootstrap.preflight.PreflightCheck;
import org.graylog2.bootstrap.preflight.PreflightCheckException;
import org.slf4j.Logger;
@@ -30,34 +28,37 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
public class OpensearchConfigSync implements PreflightCheck {
private static final Logger LOG = LoggerFactory.getLogger(OpensearchConfigSync.class);
private final Configuration configuration;
private final DatanodeConfiguration configuration;
@Inject
public OpensearchConfigSync(Configuration configuration) {
this.configuration = configuration;
public OpensearchConfigSync(DatanodeConfiguration datanodeConfiguration) {
this.configuration = datanodeConfiguration;
}
@Override
public void runCheck() throws PreflightCheckException {
final Path localOpensearchConfigDir = Path.of(configuration.getOpensearchConfigLocation()).resolve("opensearch");
LOG.info("Directory used for Opensearch configuration is {}", localOpensearchConfigDir.toAbsolutePath());
final Path opensearchProcessConfigurationDir = configuration.datanodeDirectories().getOpensearchProcessConfigurationDir();
LOG.info("Directory used for Opensearch process configuration is {}", opensearchProcessConfigurationDir.toAbsolutePath());
try {
Files.createDirectories(localOpensearchConfigDir);
synchronizeConfig(Path.of("opensearch", "config"), localOpensearchConfigDir);
Files.createDirectories(opensearchProcessConfigurationDir);
// this is a directory in main/resources that holds all the initial configuration files needed by the opensearch
// we manage this directory in git. Generally we assume that this is a read-only location and we need to copy
// its content to a read-write location for the managed opensearch process.
// This copy happens during each opensearch process start and will override any files that already exist
// from previous runs.
final Path sourceOfInitialConfiguration = Path.of("opensearch", "config");
synchronizeConfig(sourceOfInitialConfiguration, opensearchProcessConfigurationDir);
} catch (IOException | URISyntaxException e) {
throw new RuntimeException("Failed to prepare opensearch config directory", e);

View File

@@ -25,6 +25,7 @@ import org.graylog2.security.IndexerJwtAuthTokenProvider;
*/
public record DatanodeConfiguration(
OpensearchDistributionProvider opensearchDistributionProvider,
DatanodeDirectories datanodeDirectories,
String nodeName,
int processLogsBufferSize,
IndexerJwtAuthTokenProvider indexerJwtAuthTokenProvider

View File

@@ -17,25 +17,13 @@
package org.graylog.datanode.configuration;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.OpensearchDistribution;
import org.graylog.datanode.process.OpensearchConfiguration;
import org.graylog2.plugin.Tools;
import org.graylog2.plugin.system.NodeId;
import org.graylog2.security.IndexerJwtAuthTokenProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Singleton
public class DatanodeConfigurationProvider implements Provider<DatanodeConfiguration> {
@@ -43,9 +31,14 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
private final DatanodeConfiguration datanodeConfiguration;
@Inject
public DatanodeConfigurationProvider(final Configuration localConfiguration, IndexerJwtAuthTokenProvider jwtTokenProvider, OpensearchDistributionProvider opensearchDistribution) throws IOException {
public DatanodeConfigurationProvider(
final Configuration localConfiguration,
IndexerJwtAuthTokenProvider jwtTokenProvider,
OpensearchDistributionProvider opensearchDistributionProvider,
NodeId nodeId) {
datanodeConfiguration = new DatanodeConfiguration(
opensearchDistribution,
opensearchDistributionProvider,
DatanodeDirectories.fromConfiguration(localConfiguration, nodeId),
getNodesFromConfig(localConfiguration.getDatanodeNodeName()),
localConfiguration.getProcessLogsBufferSize(),
jwtTokenProvider
@@ -53,7 +46,7 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
}
public static String getNodesFromConfig(final String configProperty) {
if(configProperty != null) return configProperty;
if (configProperty != null) return configProperty;
return Tools.getLocalCanonicalHostname();
}
@@ -61,5 +54,4 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
public DatanodeConfiguration get() {
return datanodeConfiguration;
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
package org.graylog.datanode.configuration;
import org.graylog.datanode.Configuration;
import org.graylog2.plugin.system.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
/**
* This is a collection of pointers to directories used to store data, logs and configuration of the managed opensearch.
* Each data type is additionally stored in a subdirectory named after the nodeId, to avoid unexpected collisions when
* running more datanode instances in the same machine.
*/
public class DatanodeDirectories {
private static final Logger LOG = LoggerFactory.getLogger(DatanodeDirectories.class);
private final String nodeId;
private final String dataTargetDir;
private final String logsTargetDir;
private final String configurationSourceDir;
private final String configurationTargetDir;
public DatanodeDirectories(String nodeName, String dataTargetDir, String logsTargetDir, String configurationSourceDir, String configurationTargetDir) {
this.nodeId = nodeName;
this.dataTargetDir = dataTargetDir;
this.logsTargetDir = logsTargetDir;
this.configurationSourceDir = configurationSourceDir;
this.configurationTargetDir = configurationTargetDir;
}
public static DatanodeDirectories fromConfiguration(Configuration configuration, NodeId nodeId) {
final DatanodeDirectories directories = new DatanodeDirectories(
nodeId.getNodeId(),
configuration.getOpensearchDataLocation(),
configuration.getOpensearchLogsLocation(),
configuration.getDatanodeConfigurationLocation(),
configuration.getOpensearchConfigLocation()
);
LOG.info("Opensearch of the node {} uses following directories as its storage: {}", nodeId.getNodeId(), directories);
return directories;
}
/**
* This directory is used by the managed opensearch to store its data in it.
* Read-write permissions required.
*/
public Path getDataTargetDir() {
return resolvePath(dataTargetDir);
}
/**
* This directory is used by the managed opensearch to store its logs in it.
* Read-write permissions required.
*/
public Path getLogsTargetDir() {
return resolvePath(logsTargetDir);
}
/**
* This directory is provided by system admin to the datanode. We read our configuration from this location,
* we read certificates from here. We'll never write anything to it.
* Read-only permissions required.
*/
public Path getConfigurationSourceDir() {
return Path.of(configurationSourceDir).toAbsolutePath();
}
/**
* This directory is used by us to store all runtime-generated configuration of datanode. This
* could be truststores, private keys, certificates and other generated config files.
*
* We also synchronize and generate opensearch configuration into a subdir of this dir, see {@link #getOpensearchProcessConfigurationDir()}
* Read-write permissions required.
*/
public Path getConfigurationTargetDir() {
return resolvePath(configurationTargetDir);
}
/**
* This is a subdirectory of {@link #getConfigurationTargetDir()}. It's used by us to synchronize and generate opensearch
* configuration. Opensearch is then instructed to accept this dir as its base configuration dir (OPENSEARCH_PATH_CONF env property).
* @see org.graylog.datanode.bootstrap.preflight.OpensearchConfigSync
*/
public Path getOpensearchProcessConfigurationDir() {
return resolvePath(configurationTargetDir).resolve("opensearch");
}
private Path resolvePath(String path) {
return Path.of(path).resolve(nodeId).toAbsolutePath();
}
@Override
public String toString() {
return "DatanodeDirectories{" +
"dataTargetDir='" + getDataTargetDir() + '\'' +
", logsTargetDir='" + getLogsTargetDir() + '\'' +
", configurationSourceDir='" + getConfigurationSourceDir() + '\'' +
", configurationTargetDir='" + getConfigurationTargetDir() + '\'' +
", opensearchProcessConfigurationDir='" + getOpensearchProcessConfigurationDir() + '\'' +
'}';
}
}

View File

@@ -25,7 +25,6 @@ import org.graylog.datanode.configuration.variants.SecurityConfigurationVariant;
import org.graylog.datanode.configuration.variants.UploadedCertFilesSecureConfiguration;
import org.graylog.datanode.process.OpensearchConfiguration;
import org.graylog.security.certutil.ca.exceptions.KeyStoreStorageException;
import org.graylog2.bootstrap.preflight.PreflightConfig;
import org.graylog2.bootstrap.preflight.PreflightConfigResult;
import org.graylog2.bootstrap.preflight.PreflightConfigService;
import org.graylog2.cluster.Node;
@@ -40,7 +39,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Singleton
@@ -112,13 +110,13 @@ public class OpensearchConfigurationProvider implements Provider<OpensearchConfi
if (chosenSecurityConfigurationVariant.isPresent()) {
securityConfiguration = chosenSecurityConfigurationVariant.get()
.build()
.configure(localConfiguration, signingKey);
.configure(datanodeConfiguration, signingKey);
opensearchProperties.putAll(securityConfiguration.getProperties());
}
return new OpensearchConfiguration(
datanodeConfiguration.opensearchDistributionProvider().get().directory(),
opensearchConfigLocation,
datanodeConfiguration.opensearchDistributionProvider().get(),
datanodeConfiguration.datanodeDirectories(),
localConfiguration.getBindAddress(),
localConfiguration.getHostname(),
localConfiguration.getOpensearchHttpPort(),
@@ -137,7 +135,6 @@ public class OpensearchConfigurationProvider implements Provider<OpensearchConfi
private ImmutableMap<String, String> commonOpensearchConfig(final Configuration localConfiguration) {
final ImmutableMap.Builder<String, String> config = ImmutableMap.builder();
Objects.requireNonNull(localConfiguration.getConfigLocation(), "config_location setting is required!");
localConfiguration.getOpensearchNetworkHostHost().ifPresent(
networkHost -> config.put("network.host", networkHost));
config.put("path.data", Path.of(localConfiguration.getOpensearchDataLocation()).resolve(nodeName).toAbsolutePath().toString());

View File

@@ -17,6 +17,7 @@
package org.graylog.datanode.configuration.variants;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.datanode.configuration.certificates.KeystoreReEncryption;
import org.graylog.security.certutil.ca.exceptions.KeyStoreStorageException;
import org.graylog.security.certutil.keystore.storage.location.KeystoreFileLocation;
@@ -44,12 +45,13 @@ public final class MongoCertSecureConfiguration extends SecureConfiguration {
@Inject
public MongoCertSecureConfiguration(final Configuration localConfiguration,
final DatanodeConfiguration datanodeConfiguration,
final KeystoreReEncryption keystoreReEncryption,
final NodeId nodeId,
final @Named("password_secret") String passwordSecret,
final CertificatesService certificatesService
) {
super(localConfiguration);
super(datanodeConfiguration);
this.keystoreReEncryption = keystoreReEncryption;
this.certificatesService = certificatesService;

View File

@@ -22,6 +22,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.RandomStringUtils;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.datanode.configuration.TruststoreCreator;
import org.graylog.security.certutil.CertConstants;
import org.slf4j.Logger;
@@ -73,13 +74,13 @@ public class OpensearchSecurityConfiguration {
* initial set of opensearch users, it will create and persist a truststore that will be set as a system-wide
* truststore.
*/
public OpensearchSecurityConfiguration configure(Configuration localConfiguration, byte[] signingKey) throws GeneralSecurityException, IOException {
public OpensearchSecurityConfiguration configure(DatanodeConfiguration datanodeConfiguration, byte[] signingKey) throws GeneralSecurityException, IOException {
if (securityEnabled()) {
logCertificateInformation("transport certificate", transportCertificate);
logCertificateInformation("HTTP certificate", httpCertificate);
final Path opensearchConfigDir = Path.of(localConfiguration.getOpensearchConfigLocation()).resolve("opensearch");
final Path opensearchConfigDir = datanodeConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir();
final Path trustStorePath = opensearchConfigDir.resolve(TRUSTSTORE_FILENAME);
final String truststorePassword = RandomStringUtils.randomAlphabetic(256);

View File

@@ -16,7 +16,7 @@
*/
package org.graylog.datanode.configuration.variants;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import java.nio.file.Path;
@@ -25,8 +25,8 @@ public sealed abstract class SecureConfiguration implements SecurityConfiguratio
final Path datanodeConfigDir;
final Path opensearchConfigDir;
public SecureConfiguration(final Configuration localConfiguration) {
this.opensearchConfigDir = Path.of(localConfiguration.getOpensearchConfigLocation()).resolve("opensearch");
this.datanodeConfigDir = Path.of(localConfiguration.getConfigLocation());
public SecureConfiguration(final DatanodeConfiguration localConfiguration) {
this.datanodeConfigDir = localConfiguration.datanodeDirectories().getConfigurationSourceDir();
this.opensearchConfigDir = localConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir();
}
}

View File

@@ -17,6 +17,7 @@
package org.graylog.datanode.configuration.variants;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.configuration.DatanodeConfiguration;
import org.graylog.datanode.configuration.OpensearchConfigurationException;
import org.graylog.datanode.configuration.certificates.KeystoreReEncryption;
import org.graylog.datanode.configuration.verification.ConfigProperty;
@@ -49,9 +50,10 @@ public final class UploadedCertFilesSecureConfiguration extends SecureConfigurat
@Inject
public UploadedCertFilesSecureConfiguration(final Configuration localConfiguration,
final DatanodeConfiguration datanodeConfiguration,
final KeystoreReEncryption keystoreReEncryption,
final ConfigSectionCompletenessVerifier configSectionCompletenessVerifier) {
super(localConfiguration);
super(datanodeConfiguration);
this.keystoreReEncryption = keystoreReEncryption;
this.configSectionCompletenessVerifier = configSectionCompletenessVerifier;
this.uploadedTransportKeystorePath = datanodeConfigDir

View File

@@ -30,7 +30,6 @@ import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -49,7 +48,7 @@ public class OpensearchCommandLineProcess implements Closeable {
*/
private void fixJdkOnMac(final OpensearchConfiguration config) {
final var isMacOS = OS.isFamilyMac();
final var jdk = config.opensearchDir().resolve("jdk.app");
final var jdk = config.opensearchDistribution().directory().resolve("jdk.app");
final var jdkNotLinked = !Files.exists(jdk);
if (isMacOS && jdkNotLinked) {
// Link System jdk into startup folder, get path:
@@ -77,7 +76,7 @@ public class OpensearchCommandLineProcess implements Closeable {
public OpensearchCommandLineProcess(OpensearchConfiguration config, ProcessListener listener) {
fixJdkOnMac(config);
final Path executable = config.opensearchDir().resolve(Paths.get("bin", "opensearch"));
final Path executable = config.opensearchDistribution().getOpensearchExecutable();;
final List<String> arguments = getOpensearchConfigurationArguments(config).entrySet().stream()
.map(it -> String.format(Locale.ROOT, "-E%s=%s", it.getKey(), it.getValue())).toList();
resultHandler = new CommandLineProcessListener(listener);

View File

@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -86,7 +85,7 @@ class OpensearchProcessImpl implements OpensearchProcess, ProcessListener {
this.trustManager = trustManager;
this.nodeService = nodeService;
this.configuration = configuration;
this.hostsfile = Path.of(configuration.getOpensearchConfigLocation()).resolve("opensearch").resolve("unicast_hosts.txt");
this.hostsfile = datanodeConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir().resolve("unicast_hosts.txt");
}
private RestHighLevelClient createRestClient(OpensearchConfiguration configuration) {

View File

@@ -17,18 +17,19 @@
package org.graylog.datanode.process;
import org.apache.commons.exec.OS;
import org.graylog.datanode.OpensearchDistribution;
import org.graylog.datanode.configuration.DatanodeDirectories;
import org.graylog.datanode.configuration.variants.OpensearchSecurityConfiguration;
import org.graylog.datanode.management.Environment;
import org.graylog.shaded.opensearch2.org.apache.http.HttpHost;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public record OpensearchConfiguration(
Path opensearchDir,
Path opensearchConfigDir,
OpensearchDistribution opensearchDistribution,
DatanodeDirectories datanodeDirectories,
String bindAddress,
String hostname,
int httpPort,
@@ -80,7 +81,7 @@ public record OpensearchConfiguration(
public Environment getEnv() {
final Environment env = new Environment(System.getenv());
env.put("OPENSEARCH_PATH_CONF", opensearchConfigDir.resolve("opensearch").toAbsolutePath().toString());
env.put("OPENSEARCH_PATH_CONF", datanodeDirectories.getOpensearchProcessConfigurationDir().toString());
return env;
}