Skip to content

Commit

Permalink
Switch to SslSettingsManager
Browse files Browse the repository at this point in the history
Switched to using SslSettingsManager instead of DefaultKeyStore.

Signed-off-by: Andrey Pleskach <[email protected]>
  • Loading branch information
willyborankin committed Sep 9, 2024
1 parent 2905613 commit 8d93ac9
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ public List<RestHandler> getRestHandlers(
evaluator,
threadPool,
Objects.requireNonNull(auditLog),
sks,
sslSettingsManager,
Objects.requireNonNull(userService),
sslCertReloadEnabled,
passwordHasher
Expand Down Expand Up @@ -1207,9 +1207,7 @@ public Collection<Object> createComponents(
components.add(userService);
components.add(passwordHasher);

if (!ExternalSecurityKeyStore.hasExternalSslContext(settings)) {
components.add(sks);
}
components.add(sslSettingsManager);
final var allowDefaultInit = settings.getAsBoolean(SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, false);
final var useClusterState = useClusterStateToInitSecurityConfig(settings);
if (!SSLConfig.isSslOnlyMode() && !isDisabled(settings) && allowDefaultInit && useClusterState) {
Expand Down Expand Up @@ -2122,7 +2120,7 @@ public PluginSubject getPluginSubject(Plugin plugin) {

@Override
public Optional<SecureSettingsFactory> getSecureSettingFactory(Settings settings) {
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sks, sslExceptionHandler, securityRestHandler));
return Optional.of(new OpenSearchSecureSettingsFactory(threadPool, sslSettingsManager, sslExceptionHandler, securityRestHandler));
}

@SuppressWarnings("removal")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.opensearch.security.configuration.ConfigurationRepository;
import org.opensearch.security.hasher.PasswordHasher;
import org.opensearch.security.privileges.PrivilegesEvaluator;
import org.opensearch.security.ssl.SecurityKeyStore;
import org.opensearch.security.ssl.SslSettingsManager;
import org.opensearch.security.ssl.transport.PrincipalExtractor;
import org.opensearch.security.user.UserService;
import org.opensearch.threadpool.ThreadPool;
Expand All @@ -46,7 +46,7 @@ public static Collection<RestHandler> getHandler(
final PrivilegesEvaluator evaluator,
final ThreadPool threadPool,
final AuditLog auditLog,
final SecurityKeyStore securityKeyStore,
final SslSettingsManager sslSettingsManager,
final UserService userService,
final boolean certificatesReloadEnabled,
final PasswordHasher passwordHasher
Expand Down Expand Up @@ -99,7 +99,13 @@ public static Collection<RestHandler> getHandler(
new MultiTenancyConfigApiAction(clusterService, threadPool, securityApiDependencies),
new AuthFailureListenersApiAction(clusterService, threadPool, securityApiDependencies),
new ConfigUpgradeApiAction(clusterService, threadPool, securityApiDependencies),
new SecuritySSLCertsApiAction(clusterService, threadPool, securityKeyStore, certificatesReloadEnabled, securityApiDependencies),
new SecuritySSLCertsApiAction(
clusterService,
threadPool,
sslSettingsManager,
certificatesReloadEnabled,
securityApiDependencies
),
new CertificatesApiAction(clusterService, threadPool, securityApiDependencies)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@
package org.opensearch.security.dlic.rest.api;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
Expand All @@ -31,8 +30,9 @@
import org.opensearch.rest.RestRequest.Method;
import org.opensearch.security.dlic.rest.validation.ValidationResult;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.ssl.SecurityKeyStore;
import org.opensearch.security.ssl.util.SSLConfigConstants;
import org.opensearch.security.ssl.SslContextHandler;
import org.opensearch.security.ssl.SslSettingsManager;
import org.opensearch.security.ssl.config.Certificate;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.threadpool.ThreadPool;

Expand All @@ -43,6 +43,9 @@
import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.CERTS_INFO_ACTION;
import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.RELOAD_CERTS_ACTION;
import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;
import static org.opensearch.security.ssl.util.SSLConfigConstants.SSL_HTTP_PREFIX;
import static org.opensearch.security.ssl.util.SSLConfigConstants.SSL_TRANSPORT_CLIENT_PREFIX;
import static org.opensearch.security.ssl.util.SSLConfigConstants.SSL_TRANSPORT_PREFIX;

/**
* Rest API action to get SSL certificate information related to http and transport encryption.
Expand All @@ -62,23 +65,20 @@ public class SecuritySSLCertsApiAction extends AbstractApiAction {
)
);

private final SecurityKeyStore securityKeyStore;
private final SslSettingsManager sslSettingsManager;

private final boolean certificatesReloadEnabled;

private final boolean httpsEnabled;

public SecuritySSLCertsApiAction(
final ClusterService clusterService,
final ThreadPool threadPool,
final SecurityKeyStore securityKeyStore,
final SslSettingsManager sslSettingsManager,
final boolean certificatesReloadEnabled,
final SecurityApiDependencies securityApiDependencies
) {
super(Endpoint.SSL, clusterService, threadPool, securityApiDependencies);
this.securityKeyStore = securityKeyStore;
this.sslSettingsManager = sslSettingsManager;
this.certificatesReloadEnabled = certificatesReloadEnabled;
this.httpsEnabled = securityApiDependencies.settings().getAsBoolean(SSLConfigConstants.SECURITY_SSL_HTTP_ENABLED, true);
this.requestHandlersBuilder.configureRequestHandlers(this::securitySSLCertsRequestHandlers);
}

Expand Down Expand Up @@ -108,10 +108,10 @@ private void securitySSLCertsRequestHandlers(RequestHandler.RequestHandlersBuild
.verifyAccessForAllMethods()
.override(
Method.GET,
(channel, request, client) -> withSecurityKeyStore().valid(keyStore -> loadCertificates(channel, keyStore))
(channel, request, client) -> withSecurityKeyStore().valid(ignore -> loadCertificates(channel))
.error((status, toXContent) -> response(channel, status, toXContent))
)
.override(Method.PUT, (channel, request, client) -> withSecurityKeyStore().valid(keyStore -> {
.override(Method.PUT, (channel, request, client) -> withSecurityKeyStore().valid(ignore -> {
if (!certificatesReloadEnabled) {
badRequest(
channel,
Expand All @@ -123,7 +123,7 @@ private void securitySSLCertsRequestHandlers(RequestHandler.RequestHandlersBuild
)
);
} else {
reloadCertificates(channel, request, keyStore);
reloadCertificates(channel, request);
}
}).error((status, toXContent) -> response(channel, status, toXContent)));
}
Expand All @@ -138,65 +138,70 @@ boolean accessHandler(final RestRequest request) {
}
}

ValidationResult<SecurityKeyStore> withSecurityKeyStore() {
if (securityKeyStore == null) {
ValidationResult<SslSettingsManager> withSecurityKeyStore() {
if (sslSettingsManager == null) {
return ValidationResult.error(RestStatus.OK, badRequestMessage("keystore is not initialized"));
}
return ValidationResult.success(securityKeyStore);
return ValidationResult.success(sslSettingsManager);
}

protected void loadCertificates(final RestChannel channel, final SecurityKeyStore keyStore) throws IOException {
protected void loadCertificates(final RestChannel channel) throws IOException {
ok(
channel,
(builder, params) -> builder.startObject()
.field("http_certificates_list", httpsEnabled ? generateCertDetailList(keyStore.getHttpCerts()) : null)
.field("transport_certificates_list", generateCertDetailList(keyStore.getTransportCerts()))
.field(
"http_certificates_list",
generateCertDetailList(
sslSettingsManager.sslContextHandler(SSL_HTTP_PREFIX).map(SslContextHandler::keyMaterialCertificates).orElse(null)
)
)
.field(
"transport_certificates_list",
generateCertDetailList(
sslSettingsManager.sslContextHandler(SSL_TRANSPORT_PREFIX)
.map(SslContextHandler::keyMaterialCertificates)
.orElse(null)
)
)
.endObject()
);
}

private List<Map<String, String>> generateCertDetailList(final X509Certificate[] certs) {
private List<Map<String, String>> generateCertDetailList(final Stream<Certificate> certs) {
if (certs == null) {
return null;
}
return Arrays.stream(certs).map(cert -> {
final String issuerDn = cert != null && cert.getIssuerX500Principal() != null ? cert.getIssuerX500Principal().getName() : "";
final String subjectDn = cert != null && cert.getSubjectX500Principal() != null ? cert.getSubjectX500Principal().getName() : "";

final String san = securityKeyStore.getSubjectAlternativeNames(cert);

final String notBefore = cert != null && cert.getNotBefore() != null ? cert.getNotBefore().toInstant().toString() : "";
final String notAfter = cert != null && cert.getNotAfter() != null ? cert.getNotAfter().toInstant().toString() : "";
return ImmutableMap.of(
return certs.map(
c -> ImmutableMap.of(
"issuer_dn",
issuerDn,
c.issuer(),
"subject_dn",
subjectDn,
c.subject(),
"san",
san,
c.subjectAlternativeNames(),
"not_before",
notBefore,
c.notBefore(),
"not_after",
notAfter
);
}).collect(Collectors.toList());
c.notAfter()
)
).collect(Collectors.toList());
}

protected void reloadCertificates(final RestChannel channel, final RestRequest request, final SecurityKeyStore keyStore)
throws IOException {
protected void reloadCertificates(final RestChannel channel, final RestRequest request) throws IOException {
final String certType = request.param("certType").toLowerCase().trim();
try {
switch (certType) {
case "http":
if (!httpsEnabled) {
if (sslSettingsManager.sslConfiguration(SSL_HTTP_PREFIX).isPresent()) {
sslSettingsManager.reloadSslContext(SSL_HTTP_PREFIX);
ok(channel, (builder, params) -> builder.startObject().field("message", "updated http certs").endObject());
} else {
badRequest(channel, "SSL for HTTP is disabled");
return;
}
keyStore.initHttpSSLConfig();
ok(channel, (builder, params) -> builder.startObject().field("message", "updated http certs").endObject());
break;
case "transport":
keyStore.initTransportSSLConfig();
sslSettingsManager.reloadSslContext(SSL_TRANSPORT_PREFIX);
sslSettingsManager.reloadSslContext(SSL_TRANSPORT_CLIENT_PREFIX);
ok(channel, (builder, params) -> builder.startObject().field("message", "updated transport certs").endObject());
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,43 @@
package org.opensearch.security.dlic.rest.api.ssl;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.opensearch.action.FailedNodeException;
import org.opensearch.action.support.ActionFilters;
import org.opensearch.action.support.nodes.TransportNodesAction;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.inject.Inject;
import org.opensearch.common.settings.Settings;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.security.ssl.DefaultSecurityKeyStore;
import org.opensearch.security.ssl.util.SSLConfigConstants;
import org.opensearch.security.ssl.SslContextHandler;
import org.opensearch.security.ssl.SslSettingsManager;
import org.opensearch.security.ssl.config.Certificate;
import org.opensearch.threadpool.ThreadPool;
import org.opensearch.transport.TransportRequest;
import org.opensearch.transport.TransportService;

import static org.opensearch.security.ssl.util.SSLConfigConstants.SSL_HTTP_PREFIX;
import static org.opensearch.security.ssl.util.SSLConfigConstants.SSL_TRANSPORT_PREFIX;

public class TransportCertificatesInfoNodesAction extends TransportNodesAction<
CertificatesInfoNodesRequest,
CertificatesNodesResponse,
TransportCertificatesInfoNodesAction.NodeRequest,
CertificatesNodesResponse.CertificatesNodeResponse> {

private final DefaultSecurityKeyStore securityKeyStore;

private final boolean httpsEnabled;
private final SslSettingsManager sslSettingsManager;

@Inject
public TransportCertificatesInfoNodesAction(
final Settings settings,
final ThreadPool threadPool,
final ClusterService clusterService,
final TransportService transportService,
final ActionFilters actionFilters,
final DefaultSecurityKeyStore securityKeyStore
final SslSettingsManager sslSettingsManager
) {
super(
CertificatesActionType.NAME,
Expand All @@ -62,8 +61,7 @@ public TransportCertificatesInfoNodesAction(
ThreadPool.Names.GENERIC,
CertificatesNodesResponse.CertificatesNodeResponse.class
);
this.httpsEnabled = settings.getAsBoolean(SSLConfigConstants.SECURITY_SSL_HTTP_ENABLED, true);
this.securityKeyStore = securityKeyStore;
this.sslSettingsManager = sslSettingsManager;
}

@Override
Expand All @@ -89,12 +87,6 @@ protected CertificatesNodesResponse.CertificatesNodeResponse newNodeResponse(fin
protected CertificatesNodesResponse.CertificatesNodeResponse nodeOperation(final NodeRequest request) {
final var sslCertRequest = request.sslCertsInfoNodesRequest;

if (securityKeyStore == null) {
return new CertificatesNodesResponse.CertificatesNodeResponse(
clusterService.localNode(),
new IllegalStateException("keystore is not initialized")
);
}
try {
return new CertificatesNodesResponse.CertificatesNodeResponse(
clusterService.localNode(),
Expand All @@ -109,23 +101,27 @@ protected CertificatesInfo loadCertificates(final CertificateType certificateTyp
var httpCertificates = List.<CertificateInfo>of();
var transportsCertificates = List.<CertificateInfo>of();
if (CertificateType.isHttp(certificateType)) {
httpCertificates = httpsEnabled ? certificatesDetails(securityKeyStore.getHttpCerts()) : List.of();
httpCertificates = sslSettingsManager.sslContextHandler(SSL_HTTP_PREFIX)
.map(SslContextHandler::keyMaterialCertificates)
.map(this::certificatesDetails)
.orElse(List.of());
}
if (CertificateType.isTransport(certificateType)) {
transportsCertificates = certificatesDetails(securityKeyStore.getTransportCerts());
transportsCertificates = sslSettingsManager.sslContextHandler(SSL_TRANSPORT_PREFIX)
.map(SslContextHandler::keyMaterialCertificates)
.map(this::certificatesDetails)
.orElse(List.of());
}
return new CertificatesInfo(Map.of(CertificateType.HTTP, httpCertificates, CertificateType.TRANSPORT, transportsCertificates));
}

private List<CertificateInfo> certificatesDetails(final X509Certificate[] certs) {
if (certs == null) {
private List<CertificateInfo> certificatesDetails(final Stream<Certificate> certificateStream) {
if (certificateStream == null) {
return null;
}
final var certificates = ImmutableList.<CertificateInfo>builder();
for (final var c : certs) {
certificates.add(CertificateInfo.from(c, securityKeyStore.getSubjectAlternativeNames(c)));
}
return certificates.build();
return certificateStream.map(
c -> new CertificateInfo(c.subject(), c.subjectAlternativeNames(), c.issuer(), c.notAfter(), c.notBefore())
).collect(Collectors.toList());
}

public static class NodeRequest extends TransportRequest {
Expand Down
Loading

0 comments on commit 8d93ac9

Please sign in to comment.