CSR and certificate exchange refactored (#19545)

* Datanode keystore implementation

* Bind keystore check, remove legacy private key storage

* added license

* code cleanup, better cert signed verification

* added changelog

* code cleanup

* CSR and certificate exchange refactored

* removed jetbrains annotation

* removed jetbrains annotation
This commit is contained in:
Tomas Dvorak
2024-06-07 10:08:26 +02:00
committed by GitHub
parent 0fdd3f2828
commit 347e5653c3
13 changed files with 492 additions and 324 deletions

View File

@@ -19,16 +19,14 @@ package org.graylog.datanode.bootstrap.preflight;
import com.google.common.collect.ImmutableList;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.graylog.datanode.configuration.DatanodeKeystore;
import org.graylog.datanode.configuration.DatanodeKeystoreException;
import org.graylog.security.certutil.cert.CertificateChain;
import org.graylog.security.certutil.cert.storage.CertChainMongoStorage;
import org.graylog.security.certutil.cert.storage.CertChainStorage;
import org.graylog.security.certutil.csr.exceptions.CSRGenerationException;
import org.graylog.security.certutil.csr.storage.CsrMongoStorage;
import org.graylog2.cluster.NodeNotFoundException;
import org.graylog2.cluster.certificates.CertificateExchange;
import org.graylog2.cluster.certificates.CertificateSigningRequest;
import org.graylog2.cluster.nodes.DataNodeDto;
import org.graylog2.cluster.nodes.NodeService;
import org.graylog2.cluster.preflight.DataNodeProvisioningConfig;
@@ -54,23 +52,20 @@ public class DataNodeConfigurationPeriodical extends Periodical {
private final DataNodeProvisioningService dataNodeProvisioningService;
private final NodeService<DataNodeDto> nodeService;
private final NodeId nodeId;
private final CsrMongoStorage csrStorage;
private final CertChainStorage certMongoStorage;
private final CertificateExchange certificateExchange;
private final DatanodeKeystore datanodeKeystore;
@Inject
public DataNodeConfigurationPeriodical(final DataNodeProvisioningService dataNodeProvisioningService,
final NodeService<DataNodeDto> nodeService,
final NodeId nodeId,
final CsrMongoStorage csrStorage,
final CertChainMongoStorage certMongoStorage,
CertificateExchange certificateExchange,
DatanodeKeystore datanodeKeystore) {
this.dataNodeProvisioningService = dataNodeProvisioningService;
this.nodeService = nodeService;
this.nodeId = nodeId;
this.csrStorage = csrStorage;
this.certMongoStorage = certMongoStorage;
this.certificateExchange = certificateExchange;
this.datanodeKeystore = datanodeKeystore;
}
@@ -88,28 +83,18 @@ public class DataNodeConfigurationPeriodical extends Periodical {
if (state == null) {
return;
}
// always check if there are any certificates that we can accept
certificateExchange.pollCertificate(nodeId.getNodeId(), this::processCertificateChain);
switch (state) {
case CONFIGURED -> writeCsr(c);
case SIGNED -> readSignedCertificate(c);
case STARTUP_TRIGGER ->
dataNodeProvisioningService.changeState(nodeId.getNodeId(), DataNodeProvisioningConfig.State.STARTUP_REQUESTED);
}
});
}
private void readSignedCertificate(DataNodeProvisioningConfig cfg) {
if (cfg.certificate() == null) {
LOG.error("Config entry in signed state, but no certificate data present in Mongo");
} else {
try {
certMongoStorage.readCertChain(nodeId.getNodeId())
.ifPresent(this::processCertificateChain);
} catch (Exception ex) {
LOG.error("Config entry in signed state, but wrong certificate data present in Mongo");
}
}
}
private void processCertificateChain(CertificateChain certificateChain) {
try {
datanodeKeystore.replaceCertificatesInKeystore(certificateChain);
@@ -127,11 +112,9 @@ public class DataNodeConfigurationPeriodical extends Periodical {
.addAll(determineAltNames())
.build();
final PKCS10CertificationRequest csr = datanodeKeystore.createCertificateSigningRequest(node.getHostname(), altNames);
csrStorage.writeCsr(csr, nodeId.getNodeId());
certificateExchange.requestCertificate(new CertificateSigningRequest(nodeId.getNodeId(), csr));
LOG.info("created CSR for this node");
} catch (CSRGenerationException | IOException | NodeNotFoundException | OperatorException |
DatanodeKeystoreException
ex) {
} catch (CSRGenerationException | IOException | NodeNotFoundException | DatanodeKeystoreException ex) {
LOG.error("error generating a CSR: " + ex.getMessage(), ex);
dataNodeProvisioningService.save(cfg.asError(ex.getMessage()));
}

View File

@@ -25,6 +25,7 @@ import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.spi.Message;
import com.mongodb.MongoException;
import jakarta.inject.Inject;
import org.graylog.datanode.Configuration;
import org.graylog.datanode.bindings.ConfigurationModule;
import org.graylog.datanode.bindings.PeriodicalBindings;
@@ -34,9 +35,7 @@ import org.graylog.datanode.bootstrap.ServerBootstrap;
import org.graylog.datanode.configuration.S3RepositoryConfiguration;
import org.graylog.datanode.rest.RestBindings;
import org.graylog.datanode.shutdown.GracefulShutdown;
import org.graylog.plugins.map.config.GeoIpProcessorConfig;
import org.graylog2.bindings.MongoDBModule;
import org.graylog2.bindings.PasswordAlgorithmBindings;
import org.graylog2.cluster.nodes.DataNodeDto;
import org.graylog2.cluster.nodes.DataNodeStatus;
import org.graylog2.cluster.nodes.NodeService;
@@ -53,8 +52,6 @@ import org.graylog2.shared.system.activities.ActivityWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.inject.Inject;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;