From 7afc7fcced87e195153a8e8c3c64ec36c74f28a9 Mon Sep 17 00:00:00 2001
From: Andriy Redko <andriy.redko@aiven.io>
Date: Thu, 22 Feb 2024 16:46:11 -0500
Subject: [PATCH] [FEATURE] Built-in secure transports support

Signed-off-by: Andriy Redko <andriy.redko@aiven.io>
---
 .../ssl/SecureNetty4HttpServerTransport.java  | 149 +++++++++
 .../transport/Netty4ModulePlugin.java         |  64 ++++
 .../netty4/ssl/DualModeSslHandler.java        | 101 ++++++
 .../netty4/ssl/SecureConnectionTestUtil.java  | 212 +++++++++++++
 .../ssl/SecureNetty4ServerTransport.java      | 298 ++++++++++++++++++
 .../transport/netty4/ssl/SslUtils.java        |  75 +++++
 .../common/network/NetworkModule.java         |  19 ++
 .../common/settings/ClusterSettings.java      |   3 +
 .../org/opensearch/plugins/NetworkPlugin.java |  37 +++
 .../java/org/opensearch/plugins/Plugin.java   |   9 +
 .../plugins/SecureSettingFactory.java         |  29 ++
 .../plugins/SecureSettingProvider.java        |  55 ++++
 12 files changed, 1051 insertions(+)
 create mode 100644 modules/transport-netty4/src/main/java/org/opensearch/http/netty4/ssl/SecureNetty4HttpServerTransport.java
 create mode 100644 modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/DualModeSslHandler.java
 create mode 100644 modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureConnectionTestUtil.java
 create mode 100644 modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureNetty4ServerTransport.java
 create mode 100644 modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SslUtils.java
 create mode 100644 server/src/main/java/org/opensearch/plugins/SecureSettingFactory.java
 create mode 100644 server/src/main/java/org/opensearch/plugins/SecureSettingProvider.java

diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/ssl/SecureNetty4HttpServerTransport.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/ssl/SecureNetty4HttpServerTransport.java
new file mode 100644
index 0000000000000..e5db7d6ef4497
--- /dev/null
+++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/ssl/SecureNetty4HttpServerTransport.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2015-2017 floragunn GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.opensearch.http.netty4.ssl;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.opensearch.common.network.NetworkService;
+import org.opensearch.common.settings.ClusterSettings;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.common.util.BigArrays;
+import org.opensearch.core.xcontent.NamedXContentRegistry;
+import org.opensearch.http.HttpChannel;
+import org.opensearch.http.HttpHandlingSettings;
+import org.opensearch.http.netty4.Netty4HttpChannel;
+import org.opensearch.http.netty4.Netty4HttpServerTransport;
+import org.opensearch.plugins.SecureSettingProvider;
+import org.opensearch.telemetry.tracing.Tracer;
+import org.opensearch.threadpool.ThreadPool;
+import org.opensearch.transport.SharedGroupFactory;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.DecoderException;
+import io.netty.handler.ssl.ApplicationProtocolNames;
+import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
+import io.netty.handler.ssl.SslHandler;
+
+public class SecureNetty4HttpServerTransport extends Netty4HttpServerTransport {
+    private static final Logger logger = LogManager.getLogger(SecureNetty4HttpServerTransport.class);
+    private final SecureSettingProvider ssp;
+
+    public SecureNetty4HttpServerTransport(
+        final Settings settings,
+        final NetworkService networkService,
+        final BigArrays bigArrays,
+        final ThreadPool threadPool,
+        final NamedXContentRegistry namedXContentRegistry,
+        final Dispatcher dispatcher,
+        final ClusterSettings clusterSettings,
+        final SharedGroupFactory sharedGroupFactory,
+        final SecureSettingProvider ssp,
+        final Tracer tracer
+    ) {
+        super(
+            settings,
+            networkService,
+            bigArrays,
+            threadPool,
+            namedXContentRegistry,
+            dispatcher,
+            clusterSettings,
+            sharedGroupFactory,
+            tracer
+        );
+        this.ssp = ssp;
+    }
+
+    @Override
+    public ChannelHandler configureServerChannelHandler() {
+        return new SslHttpChannelHandler(this, handlingSettings);
+    }
+
+    @Override
+    public void onException(HttpChannel channel, Exception cause0) {
+        Throwable cause = cause0;
+
+        if (cause0 instanceof DecoderException && cause0 != null) {
+            cause = cause0.getCause();
+        }
+
+        logger.error("Exception during establishing a SSL connection: " + cause, cause);
+        super.onException(channel, cause0);
+    }
+
+    protected class SslHttpChannelHandler extends Netty4HttpServerTransport.HttpChannelHandler {
+        /**
+         * Application negotiation handler to select either HTTP 1.1 or HTTP 2 protocol, based
+         * on client/server ALPN negotiations.
+         */
+        private class Http2OrHttpHandler extends ApplicationProtocolNegotiationHandler {
+            protected Http2OrHttpHandler() {
+                super(ApplicationProtocolNames.HTTP_1_1);
+            }
+
+            @Override
+            protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
+                if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
+                    configureDefaultHttp2Pipeline(ctx.pipeline());
+                } else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
+                    configureDefaultHttpPipeline(ctx.pipeline());
+                } else {
+                    throw new IllegalStateException("Unknown application protocol: " + protocol);
+                }
+            }
+
+            @Override
+            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+                super.exceptionCaught(ctx, cause);
+                Netty4HttpChannel channel = ctx.channel().attr(HTTP_CHANNEL_KEY).get();
+                if (channel != null) {
+                    if (cause instanceof Error) {
+                        onException(channel, new Exception(cause));
+                    } else {
+                        onException(channel, (Exception) cause);
+                    }
+                }
+            }
+        }
+
+        protected SslHttpChannelHandler(final Netty4HttpServerTransport transport, final HttpHandlingSettings handlingSettings) {
+            super(transport, handlingSettings);
+        }
+
+        @Override
+        protected void initChannel(Channel ch) throws Exception {
+            super.initChannel(ch);
+
+            final SSLEngine sslEngine = ssp.buildSecureHttpEngine(settings, SecureNetty4HttpServerTransport.this)
+                .orElseGet(SSLContext.getDefault()::createSSLEngine);
+
+            final SslHandler sslHandler = new SslHandler(sslEngine);
+            ch.pipeline().addFirst("ssl_http", sslHandler);
+        }
+
+        @Override
+        protected void configurePipeline(Channel ch) {
+            ch.pipeline().addLast(new Http2OrHttpHandler());
+        }
+    }
+}
diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4ModulePlugin.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4ModulePlugin.java
index 2bc795d11ed5d..ea08e8228738d 100644
--- a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4ModulePlugin.java
+++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4ModulePlugin.java
@@ -46,11 +46,14 @@
 import org.opensearch.core.xcontent.NamedXContentRegistry;
 import org.opensearch.http.HttpServerTransport;
 import org.opensearch.http.netty4.Netty4HttpServerTransport;
+import org.opensearch.http.netty4.ssl.SecureNetty4HttpServerTransport;
 import org.opensearch.plugins.NetworkPlugin;
 import org.opensearch.plugins.Plugin;
+import org.opensearch.plugins.SecureSettingProvider;
 import org.opensearch.telemetry.tracing.Tracer;
 import org.opensearch.threadpool.ThreadPool;
 import org.opensearch.transport.netty4.Netty4Transport;
+import org.opensearch.transport.netty4.ssl.SecureNetty4ServerTransport;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -61,7 +64,9 @@
 public class Netty4ModulePlugin extends Plugin implements NetworkPlugin {
 
     public static final String NETTY_TRANSPORT_NAME = "netty4";
+    public static final String NETTY_SECURE_TRANSPORT_NAME = "netty4-secure";
     public static final String NETTY_HTTP_TRANSPORT_NAME = "netty4";
+    public static final String NETTY_SECURE_HTTP_TRANSPORT_NAME = "netty4-secure";
 
     private final SetOnce<SharedGroupFactory> groupFactory = new SetOnce<>();
 
@@ -144,6 +149,65 @@ public Map<String, Supplier<HttpServerTransport>> getHttpTransports(
         );
     }
 
+    @Override
+    public Map<String, Supplier<HttpServerTransport>> getSecureHttpTransports(
+        Settings settings,
+        ThreadPool threadPool,
+        BigArrays bigArrays,
+        PageCacheRecycler pageCacheRecycler,
+        CircuitBreakerService circuitBreakerService,
+        NamedXContentRegistry xContentRegistry,
+        NetworkService networkService,
+        HttpServerTransport.Dispatcher dispatcher,
+        ClusterSettings clusterSettings,
+        SecureSettingProvider ssp,
+        Tracer tracer
+    ) {
+        return Collections.singletonMap(
+            NETTY_SECURE_HTTP_TRANSPORT_NAME,
+            () -> new SecureNetty4HttpServerTransport(
+                settings,
+                networkService,
+                bigArrays,
+                threadPool,
+                xContentRegistry,
+                dispatcher,
+                clusterSettings,
+                getSharedGroupFactory(settings),
+                ssp,
+                tracer
+            )
+        );
+    }
+
+    @Override
+    public Map<String, Supplier<Transport>> getSecureTransports(
+        Settings settings,
+        ThreadPool threadPool,
+        PageCacheRecycler pageCacheRecycler,
+        CircuitBreakerService circuitBreakerService,
+        NamedWriteableRegistry namedWriteableRegistry,
+        NetworkService networkService,
+        SecureSettingProvider ssp,
+        Tracer tracer
+    ) {
+        return Collections.singletonMap(
+            NETTY_SECURE_TRANSPORT_NAME,
+            () -> new SecureNetty4ServerTransport(
+                settings,
+                Version.CURRENT,
+                threadPool,
+                networkService,
+                pageCacheRecycler,
+                namedWriteableRegistry,
+                circuitBreakerService,
+                getSharedGroupFactory(settings),
+                ssp,
+                tracer
+            )
+        );
+    }
+
     SharedGroupFactory getSharedGroupFactory(Settings settings) {
         SharedGroupFactory groupFactory = this.groupFactory.get();
         if (groupFactory != null) {
diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/DualModeSslHandler.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/DualModeSslHandler.java
new file mode 100644
index 0000000000000..46675eb29af22
--- /dev/null
+++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/DualModeSslHandler.java
@@ -0,0 +1,101 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+package org.opensearch.transport.netty4.ssl;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.plugins.SecureSettingProvider;
+import org.opensearch.transport.TcpTransport;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.ssl.SslHandler;
+
+/**
+ * Modifies the current pipeline dynamically to enable TLS
+ */
+public class DualModeSslHandler extends ByteToMessageDecoder {
+
+    private static final Logger logger = LogManager.getLogger(DualModeSslHandler.class);
+    private final Settings settings;
+    private final SecureSettingProvider ssp;
+    private final TcpTransport transport;
+    private final SslHandler providedSSLHandler;
+
+    public DualModeSslHandler(final Settings settings, final SecureSettingProvider ssp, final TcpTransport transport) {
+        this(settings, ssp, transport, null);
+    }
+
+    protected DualModeSslHandler(
+        final Settings settings,
+        final SecureSettingProvider ssp,
+        final TcpTransport transport,
+        SslHandler providedSSLHandler
+    ) {
+        this.settings = settings;
+        this.ssp = ssp;
+        this.transport = transport;
+        this.providedSSLHandler = providedSSLHandler;
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
+        // Will use the first six bytes to detect a protocol.
+        if (in.readableBytes() < 6) {
+            return;
+        }
+        int offset = in.readerIndex();
+        if (in.getCharSequence(offset, 6, StandardCharsets.UTF_8).equals(SecureConnectionTestUtil.DUAL_MODE_CLIENT_HELLO_MSG)) {
+            logger.debug("Received DualSSL Client Hello message");
+            ByteBuf responseBuffer = Unpooled.buffer(6);
+            responseBuffer.writeCharSequence(SecureConnectionTestUtil.DUAL_MODE_SERVER_HELLO_MSG, StandardCharsets.UTF_8);
+            ctx.writeAndFlush(responseBuffer).addListener(ChannelFutureListener.CLOSE);
+            return;
+        }
+
+        if (SslUtils.isTLS(in)) {
+            logger.debug("Identified request as SSL request");
+            enableSsl(ctx);
+        } else {
+            logger.debug("Identified request as non SSL request, running in HTTP mode as dual mode is enabled");
+            ctx.pipeline().remove(this);
+        }
+    }
+
+    private void enableSsl(ChannelHandlerContext ctx) throws SSLException, NoSuchAlgorithmException {
+        final SSLEngine sslEngine = ssp.buildSecureServerTransportEngine(settings, transport)
+            .orElseGet(SSLContext.getDefault()::createSSLEngine);
+
+        SslHandler sslHandler;
+        if (providedSSLHandler != null) {
+            sslHandler = providedSSLHandler;
+        } else {
+            sslHandler = new SslHandler(sslEngine);
+        }
+        ChannelPipeline p = ctx.pipeline();
+        p.addAfter("port_unification_handler", "ssl_server", sslHandler);
+        p.remove(this);
+        logger.debug("Removed port unification handler and added SSL handler as incoming request is SSL");
+    }
+}
diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureConnectionTestUtil.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureConnectionTestUtil.java
new file mode 100644
index 0000000000000..a575bc56b50c1
--- /dev/null
+++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureConnectionTestUtil.java
@@ -0,0 +1,212 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+package org.opensearch.transport.netty4.ssl;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Utility class to test if the server supports SSL connections.
+ * SSL Check will be done by sending an OpenSearch Ping to see if server is replying to pings.
+ * Following that a custom client hello message will be sent to the server, if the server
+ * side has OpenSearchPortUnificationHandler it will reply with server hello message.
+ */
+class SecureConnectionTestUtil {
+    private static final Logger logger = LogManager.getLogger(SecureConnectionTestUtil.class);
+
+    /**
+     * Return codes for SSLConnectionTestUtil.testConnection()
+     */
+    enum SSLConnectionTestResult {
+        /**
+         * OpenSearch Ping to the server failed.
+         */
+        OPENSEARCH_PING_FAILED,
+        /**
+         * Server does not support SSL.
+         */
+        SSL_NOT_AVAILABLE,
+        /**
+         * Server supports SSL.
+         */
+        SSL_AVAILABLE
+    }
+
+    public static final byte[] OPENSEARCH_PING_MSG = new byte[] {
+        (byte) 'E',
+        (byte) 'S',
+        (byte) 0xFF,
+        (byte) 0xFF,
+        (byte) 0xFF,
+        (byte) 0xFF };
+    public static final String DUAL_MODE_CLIENT_HELLO_MSG = "DUALCM";
+    public static final String DUAL_MODE_SERVER_HELLO_MSG = "DUALSM";
+    private static final int SOCKET_TIMEOUT_MILLIS = 10 * 1000;
+    private final String host;
+    private final int port;
+    private Socket overriddenSocket = null;
+    private OutputStreamWriter testOutputStreamWriter = null;
+    private InputStreamReader testInputStreamReader = null;
+
+    public SecureConnectionTestUtil(final String host, final int port) {
+        this.host = host;
+        this.port = port;
+    }
+
+    protected SecureConnectionTestUtil(
+        final String host,
+        final int port,
+        final Socket overriddenSocket,
+        final OutputStreamWriter testOutputStreamWriter,
+        final InputStreamReader testInputStreamReader
+    ) {
+        this.overriddenSocket = overriddenSocket;
+        this.testOutputStreamWriter = testOutputStreamWriter;
+        this.testInputStreamReader = testInputStreamReader;
+
+        this.host = host;
+        this.port = port;
+    }
+
+    /**
+     * Test connection to server by performing the below steps:
+     * - Send Client Hello to check if the server replies with Server Hello which indicates that Server understands SSL
+     * - Send OpenSearch Ping to check if the server replies to the OpenSearch Ping message
+     *
+     * @return SSLConnectionTestResult i.e. OPENSEARCH_PING_FAILED or SSL_NOT_AVAILABLE or SSL_AVAILABLE
+     */
+    public SSLConnectionTestResult testConnection() {
+        if (sendDualSSLClientHello()) {
+            return SSLConnectionTestResult.SSL_AVAILABLE;
+        }
+
+        if (sendOpenSearchPing()) {
+            return SSLConnectionTestResult.SSL_NOT_AVAILABLE;
+        }
+
+        return SSLConnectionTestResult.OPENSEARCH_PING_FAILED;
+    }
+
+    private boolean sendDualSSLClientHello() {
+        boolean dualSslSupported = false;
+        Socket socket = null;
+        try {
+            OutputStreamWriter outputStreamWriter;
+            InputStreamReader inputStreamReader;
+            if (overriddenSocket != null) {
+                socket = overriddenSocket;
+                outputStreamWriter = testOutputStreamWriter;
+                inputStreamReader = testInputStreamReader;
+            } else {
+                socket = new Socket(InetAddress.getByName(host), port);
+                outputStreamWriter = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8);
+                inputStreamReader = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
+            }
+
+            socket.setSoTimeout(SOCKET_TIMEOUT_MILLIS);
+            outputStreamWriter.write(DUAL_MODE_CLIENT_HELLO_MSG);
+            outputStreamWriter.flush();
+            logger.debug("Sent DualSSL Client Hello msg to {}", host);
+
+            StringBuilder sb = new StringBuilder();
+            int currentChar;
+            while ((currentChar = inputStreamReader.read()) != -1) {
+                sb.append((char) currentChar);
+            }
+
+            if (sb.toString().equals(DUAL_MODE_SERVER_HELLO_MSG)) {
+                logger.debug("Received DualSSL Server Hello msg from {}", host);
+                dualSslSupported = true;
+            }
+        } catch (IOException e) {
+            logger.debug("DualSSL client check failed for {}, exception {}", host, e.getMessage());
+        } finally {
+            logger.debug("Closing DualSSL check client socket for {}", host);
+            if (socket != null) {
+                try {
+                    socket.close();
+                } catch (IOException e) {
+                    logger.error(
+                        "Exception occurred while closing DualSSL check client socket for {}. Exception: {}",
+                        host,
+                        e.getMessage()
+                    );
+                }
+            }
+        }
+        logger.debug("dualSslClient check with server {}, server supports ssl = {}", host, dualSslSupported);
+        return dualSslSupported;
+    }
+
+    private boolean sendOpenSearchPing() {
+        boolean pingSucceeded = false;
+        Socket socket = null;
+        try {
+            if (overriddenSocket != null) {
+                socket = overriddenSocket;
+            } else {
+                socket = new Socket(InetAddress.getByName(host), port);
+            }
+
+            socket.setSoTimeout(SOCKET_TIMEOUT_MILLIS);
+            OutputStream outputStream = socket.getOutputStream();
+            InputStream inputStream = socket.getInputStream();
+
+            logger.debug("Sending OpenSearch Ping to {}", host);
+            outputStream.write(OPENSEARCH_PING_MSG);
+            outputStream.flush();
+
+            int currentByte;
+            int byteBufIndex = 0;
+            byte[] response = new byte[6];
+            while ((byteBufIndex < 6) && ((currentByte = inputStream.read()) != -1)) {
+                response[byteBufIndex] = (byte) currentByte;
+                byteBufIndex++;
+            }
+            if (byteBufIndex == 6) {
+                logger.debug("Received reply for OpenSearch Ping. from {}", host);
+                pingSucceeded = true;
+                for (int i = 0; i < 6; i++) {
+                    if (response[i] != OPENSEARCH_PING_MSG[i]) {
+                        // Unexpected byte in response
+                        logger.error("Received unexpected byte in OpenSearch Ping reply from {}", host);
+                        pingSucceeded = false;
+                        break;
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            logger.error("OpenSearch Ping failed for {}, exception: {}", host, ex.getMessage());
+        } finally {
+            logger.debug("Closing OpenSearch Ping client socket for connection to {}", host);
+            if (socket != null) {
+                try {
+                    socket.close();
+                } catch (IOException e) {
+                    logger.error("Exception occurred while closing socket for {}. Exception: {}", host, e.getMessage());
+                }
+            }
+        }
+
+        logger.debug("OpenSearch Ping check to server {} result = {}", host, pingSucceeded);
+        return pingSucceeded;
+    }
+}
diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureNetty4ServerTransport.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureNetty4ServerTransport.java
new file mode 100644
index 0000000000000..80ca6240dd34e
--- /dev/null
+++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SecureNetty4ServerTransport.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2015-2017 floragunn GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+package org.opensearch.transport.netty4.ssl;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.opensearch.ExceptionsHelper;
+import org.opensearch.OpenSearchSecurityException;
+import org.opensearch.Version;
+import org.opensearch.cluster.node.DiscoveryNode;
+import org.opensearch.common.SuppressForbidden;
+import org.opensearch.common.network.NetworkModule;
+import org.opensearch.common.network.NetworkService;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.common.util.PageCacheRecycler;
+import org.opensearch.core.common.io.stream.NamedWriteableRegistry;
+import org.opensearch.core.indices.breaker.CircuitBreakerService;
+import org.opensearch.plugins.SecureSettingProvider;
+import org.opensearch.telemetry.tracing.Tracer;
+import org.opensearch.threadpool.ThreadPool;
+import org.opensearch.transport.SharedGroupFactory;
+import org.opensearch.transport.TcpChannel;
+import org.opensearch.transport.netty4.Netty4Transport;
+import org.opensearch.transport.netty4.ssl.SecureConnectionTestUtil.SSLConnectionTestResult;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.DecoderException;
+import io.netty.handler.ssl.SslHandler;
+
+public class SecureNetty4ServerTransport extends Netty4Transport {
+
+    private static final Logger logger = LogManager.getLogger(SecureNetty4ServerTransport.class);
+    private final SecureSettingProvider ssp;
+
+    public SecureNetty4ServerTransport(
+        final Settings settings,
+        final Version version,
+        final ThreadPool threadPool,
+        final NetworkService networkService,
+        final PageCacheRecycler pageCacheRecycler,
+        final NamedWriteableRegistry namedWriteableRegistry,
+        final CircuitBreakerService circuitBreakerService,
+        final SharedGroupFactory sharedGroupFactory,
+        final SecureSettingProvider ssp,
+        final Tracer tracer
+    ) {
+        super(
+            settings,
+            version,
+            threadPool,
+            networkService,
+            pageCacheRecycler,
+            namedWriteableRegistry,
+            circuitBreakerService,
+            sharedGroupFactory,
+            tracer
+        );
+
+        this.ssp = ssp;
+    }
+
+    @Override
+    public void onException(TcpChannel channel, Exception e) {
+
+        Throwable cause = e;
+
+        if (e instanceof DecoderException && e != null) {
+            cause = e.getCause();
+        }
+
+        logger.error("Exception during establishing a SSL connection: " + cause, cause);
+
+        if (channel == null || !channel.isOpen()) {
+            throw new OpenSearchSecurityException("The provided TCP channel is invalid.", e);
+        }
+        super.onException(channel, e);
+    }
+
+    @Override
+    protected ChannelHandler getServerChannelInitializer(String name) {
+        return new SSLServerChannelInitializer(name);
+    }
+
+    @Override
+    protected ChannelHandler getClientChannelInitializer(DiscoveryNode node) {
+        return new SSLClientChannelInitializer(node);
+    }
+
+    protected class SSLServerChannelInitializer extends Netty4Transport.ServerChannelInitializer {
+
+        public SSLServerChannelInitializer(String name) {
+            super(name);
+        }
+
+        @Override
+        protected void initChannel(Channel ch) throws Exception {
+            super.initChannel(ch);
+
+            final boolean dualModeEnabled = NetworkModule.TRANSPORT_SSL_DUAL_MODE_ENABLED.get(settings);
+            if (dualModeEnabled) {
+                logger.info("SSL Dual mode enabled, using port unification handler");
+                final ChannelHandler portUnificationHandler = new DualModeSslHandler(settings, ssp, SecureNetty4ServerTransport.this);
+                ch.pipeline().addFirst("port_unification_handler", portUnificationHandler);
+            } else {
+                final SSLEngine sslEngine = ssp.buildSecureServerTransportEngine(settings, SecureNetty4ServerTransport.this)
+                    .orElseGet(SSLContext.getDefault()::createSSLEngine);
+                final SslHandler sslHandler = new SslHandler(sslEngine);
+                ch.pipeline().addFirst("ssl_server", sslHandler);
+            }
+        }
+
+        @Override
+        public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+            if (cause instanceof DecoderException && cause != null) {
+                cause = cause.getCause();
+            }
+
+            logger.error("Exception during establishing a SSL connection: " + cause, cause);
+
+            super.exceptionCaught(ctx, cause);
+        }
+    }
+
+    protected static class ClientSSLHandler extends ChannelOutboundHandlerAdapter {
+        private final Logger log = LogManager.getLogger(this.getClass());
+        private final Settings settings;
+        private final SecureSettingProvider ssp;
+        private final boolean hostnameVerificationEnabled;
+        private final boolean hostnameVerificationResovleHostName;
+
+        private ClientSSLHandler(
+            final Settings settings,
+            final SecureSettingProvider ssp,
+            final boolean hostnameVerificationEnabled,
+            final boolean hostnameVerificationResovleHostName
+        ) {
+            this.settings = settings;
+            this.ssp = ssp;
+            this.hostnameVerificationEnabled = hostnameVerificationEnabled;
+            this.hostnameVerificationResovleHostName = hostnameVerificationResovleHostName;
+        }
+
+        @Override
+        public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+            if (cause instanceof DecoderException && cause != null) {
+                cause = cause.getCause();
+            }
+
+            logger.error("Exception during establishing a SSL connection: " + cause, cause);
+
+            super.exceptionCaught(ctx, cause);
+        }
+
+        @SuppressForbidden(reason = "The java.net.InetSocketAddress#getHostName() needs to be used")
+        @Override
+        public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise)
+            throws Exception {
+            SSLEngine sslEngine = null;
+            try {
+                if (hostnameVerificationEnabled) {
+                    final InetSocketAddress inetSocketAddress = (InetSocketAddress) remoteAddress;
+                    String hostname = null;
+                    if (hostnameVerificationResovleHostName) {
+                        hostname = inetSocketAddress.getHostName();
+                    } else {
+                        hostname = inetSocketAddress.getHostString();
+                    }
+
+                    if (log.isDebugEnabled()) {
+                        log.debug(
+                            "Hostname of peer is {} ({}/{}) with hostnameVerificationResovleHostName: {}",
+                            hostname,
+                            inetSocketAddress.getHostName(),
+                            inetSocketAddress.getHostString(),
+                            hostnameVerificationResovleHostName
+                        );
+                    }
+
+                    sslEngine = ssp.buildSecureClientTransportEngine(settings, hostname, inetSocketAddress.getPort()).orElse(null);
+
+                } else {
+                    sslEngine = ssp.buildSecureClientTransportEngine(settings, null, -1).orElse(null);
+                }
+
+                if (sslEngine == null) {
+                    sslEngine = SSLContext.getDefault().createSSLEngine();
+                }
+            } catch (final SSLException e) {
+                throw ExceptionsHelper.convertToOpenSearchException(e);
+            }
+
+            final SslHandler sslHandler = new SslHandler(sslEngine);
+            ctx.pipeline().replace(this, "ssl_client", sslHandler);
+            super.connect(ctx, remoteAddress, localAddress, promise);
+        }
+    }
+
+    protected class SSLClientChannelInitializer extends Netty4Transport.ClientChannelInitializer {
+        private final boolean hostnameVerificationEnabled;
+        private final boolean hostnameVerificationResolveHostName;
+        private final DiscoveryNode node;
+        private SSLConnectionTestResult connectionTestResult;
+
+        @SuppressWarnings("removal")
+        public SSLClientChannelInitializer(DiscoveryNode node) {
+            this.node = node;
+
+            final boolean dualModeEnabled = NetworkModule.TRANSPORT_SSL_DUAL_MODE_ENABLED.get(settings);
+            hostnameVerificationEnabled = NetworkModule.TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION.get(settings);
+            hostnameVerificationResolveHostName = NetworkModule.TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME.get(settings);
+
+            connectionTestResult = SSLConnectionTestResult.SSL_AVAILABLE;
+            if (dualModeEnabled) {
+                SecureConnectionTestUtil sslConnectionTestUtil = new SecureConnectionTestUtil(
+                    node.getAddress().getAddress(),
+                    node.getAddress().getPort()
+                );
+                connectionTestResult = AccessController.doPrivileged(
+                    (PrivilegedAction<SSLConnectionTestResult>) sslConnectionTestUtil::testConnection
+                );
+            }
+        }
+
+        @Override
+        protected void initChannel(Channel ch) throws Exception {
+            super.initChannel(ch);
+
+            if (connectionTestResult == SSLConnectionTestResult.OPENSEARCH_PING_FAILED) {
+                logger.error(
+                    "SSL dual mode is enabled but dual mode handshake and OpenSearch ping has failed during client connection setup, closing channel"
+                );
+                ch.close();
+                return;
+            }
+
+            if (connectionTestResult == SSLConnectionTestResult.SSL_AVAILABLE) {
+                logger.debug("Connection to {} needs to be ssl, adding ssl handler to the client channel ", node.getHostName());
+                ch.pipeline()
+                    .addFirst(
+                        "client_ssl_handler",
+                        new ClientSSLHandler(settings, ssp, hostnameVerificationEnabled, hostnameVerificationResolveHostName)
+                    );
+            } else {
+                logger.debug("Connection to {} needs to be non ssl", node.getHostName());
+            }
+        }
+
+        @Override
+        public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+            if (cause instanceof DecoderException && cause != null) {
+                cause = cause.getCause();
+            }
+
+            logger.error("Exception during establishing a SSL connection: " + cause, cause);
+
+            super.exceptionCaught(ctx, cause);
+        }
+    }
+}
diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SslUtils.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SslUtils.java
new file mode 100644
index 0000000000000..5e752bd58ac3d
--- /dev/null
+++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/ssl/SslUtils.java
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+package org.opensearch.transport.netty4.ssl;
+
+import java.nio.ByteOrder;
+
+import io.netty.buffer.ByteBuf;
+
+class SslUtils {
+
+    private static final int SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20;
+    private static final int SSL_CONTENT_TYPE_ALERT = 21;
+    private static final int SSL_CONTENT_TYPE_HANDSHAKE = 22;
+    private static final int SSL_CONTENT_TYPE_APPLICATION_DATA = 23;
+    // CS-SUPPRESS-SINGLE: RegexpSingleline Extensions heartbeat needs special handling by security extension
+    private static final int SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT = 24;
+    // CS-ENFORCE-SINGLE
+    private static final int SSL_RECORD_HEADER_LENGTH = 5;
+
+    private SslUtils() {
+
+    }
+
+    public static boolean isTLS(ByteBuf buffer) {
+        int packetLength = 0;
+        int offset = buffer.readerIndex();
+
+        // SSLv3 or TLS - Check ContentType
+        boolean tls;
+        switch (buffer.getUnsignedByte(offset)) {
+            case SSL_CONTENT_TYPE_CHANGE_CIPHER_SPEC:
+            case SSL_CONTENT_TYPE_ALERT:
+            case SSL_CONTENT_TYPE_HANDSHAKE:
+            case SSL_CONTENT_TYPE_APPLICATION_DATA:
+                // CS-SUPPRESS-SINGLE: RegexpSingleline Extensions heartbeat needs special handling by security extension
+            case SSL_CONTENT_TYPE_EXTENSION_HEARTBEAT:
+                tls = true;
+                break;
+            // CS-ENFORCE-SINGLE
+            default:
+                // SSLv2 or bad data
+                tls = false;
+        }
+
+        if (tls) {
+            // SSLv3 or TLS - Check ProtocolVersion
+            int majorVersion = buffer.getUnsignedByte(offset + 1);
+            if (majorVersion == 3) {
+                // SSLv3 or TLS
+                packetLength = unsignedShortBE(buffer, offset + 3) + SSL_RECORD_HEADER_LENGTH;
+                if (packetLength <= SSL_RECORD_HEADER_LENGTH) {
+                    // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
+                    tls = false;
+                }
+            } else {
+                // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data)
+                tls = false;
+            }
+        }
+
+        return tls;
+    }
+
+    private static int unsignedShortBE(ByteBuf buffer, int offset) {
+        return buffer.order() == ByteOrder.BIG_ENDIAN ? buffer.getUnsignedShort(offset) : buffer.getUnsignedShortLE(offset);
+    }
+}
diff --git a/server/src/main/java/org/opensearch/common/network/NetworkModule.java b/server/src/main/java/org/opensearch/common/network/NetworkModule.java
index f97d5b2f80eeb..3c25d1d9d4ca3 100644
--- a/server/src/main/java/org/opensearch/common/network/NetworkModule.java
+++ b/server/src/main/java/org/opensearch/common/network/NetworkModule.java
@@ -85,6 +85,9 @@ public final class NetworkModule {
     public static final String HTTP_TYPE_KEY = "http.type";
     public static final String HTTP_TYPE_DEFAULT_KEY = "http.type.default";
     public static final String TRANSPORT_TYPE_DEFAULT_KEY = "transport.type.default";
+    public static final String TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_KEY = "transport.ssl.enforce_hostname_verification";
+    public static final String TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME_KEY = "transport.ssl.resolve_hostname";
+    public static final String TRANSPORT_SSL_DUAL_MODE_ENABLED_KEY = "transport.ssl.dual_mode.enabled";
 
     public static final Setting<String> TRANSPORT_DEFAULT_TYPE_SETTING = Setting.simpleString(
         TRANSPORT_TYPE_DEFAULT_KEY,
@@ -94,6 +97,22 @@ public final class NetworkModule {
     public static final Setting<String> HTTP_TYPE_SETTING = Setting.simpleString(HTTP_TYPE_KEY, Property.NodeScope);
     public static final Setting<String> TRANSPORT_TYPE_SETTING = Setting.simpleString(TRANSPORT_TYPE_KEY, Property.NodeScope);
 
+    public static final Setting<Boolean> TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION = Setting.boolSetting(
+        TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_KEY,
+        true,
+        Property.NodeScope
+    );
+    public static final Setting<Boolean> TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME = Setting.boolSetting(
+        TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME_KEY,
+        true,
+        Property.NodeScope
+    );
+    public static final Setting<Boolean> TRANSPORT_SSL_DUAL_MODE_ENABLED = Setting.boolSetting(
+        TRANSPORT_SSL_DUAL_MODE_ENABLED_KEY,
+        false,
+        Property.NodeScope
+    );
+
     private final Settings settings;
 
     private static final List<NamedWriteableRegistry.Entry> namedWriteables = new ArrayList<>();
diff --git a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java
index 896a234c115b6..26246ac02886c 100644
--- a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java
+++ b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java
@@ -328,6 +328,9 @@ public void apply(Settings value, Settings current, Settings previous) {
                 NetworkModule.TRANSPORT_DEFAULT_TYPE_SETTING,
                 NetworkModule.HTTP_TYPE_SETTING,
                 NetworkModule.TRANSPORT_TYPE_SETTING,
+                NetworkModule.TRANSPORT_SSL_DUAL_MODE_ENABLED,
+                NetworkModule.TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION,
+                NetworkModule.TRANSPORT_SSL_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME,
                 HttpTransportSettings.SETTING_CORS_ALLOW_CREDENTIALS,
                 HttpTransportSettings.SETTING_CORS_ENABLED,
                 HttpTransportSettings.SETTING_CORS_MAX_AGE,
diff --git a/server/src/main/java/org/opensearch/plugins/NetworkPlugin.java b/server/src/main/java/org/opensearch/plugins/NetworkPlugin.java
index 07df40bafe6a1..be855b151f868 100644
--- a/server/src/main/java/org/opensearch/plugins/NetworkPlugin.java
+++ b/server/src/main/java/org/opensearch/plugins/NetworkPlugin.java
@@ -107,4 +107,41 @@ default Map<String, Supplier<HttpServerTransport>> getHttpTransports(
     ) {
         return Collections.emptyMap();
     }
+
+    /**
+     * Returns a map of secure {@link Transport} suppliers.
+     * See {@link org.opensearch.common.network.NetworkModule#TRANSPORT_TYPE_KEY} to configure a specific implementation.
+     */
+    default Map<String, Supplier<Transport>> getSecureTransports(
+        Settings settings,
+        ThreadPool threadPool,
+        PageCacheRecycler pageCacheRecycler,
+        CircuitBreakerService circuitBreakerService,
+        NamedWriteableRegistry namedWriteableRegistry,
+        NetworkService networkService,
+        SecureSettingProvider sps,
+        Tracer tracer
+    ) {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Returns a map of secure {@link HttpServerTransport} suppliers.
+     * See {@link org.opensearch.common.network.NetworkModule#HTTP_TYPE_SETTING} to configure a specific implementation.
+     */
+    default Map<String, Supplier<HttpServerTransport>> getSecureHttpTransports(
+        Settings settings,
+        ThreadPool threadPool,
+        BigArrays bigArrays,
+        PageCacheRecycler pageCacheRecycler,
+        CircuitBreakerService circuitBreakerService,
+        NamedXContentRegistry xContentRegistry,
+        NetworkService networkService,
+        HttpServerTransport.Dispatcher dispatcher,
+        ClusterSettings clusterSettings,
+        SecureSettingProvider sps,
+        Tracer tracer
+    ) {
+        return Collections.emptyMap();
+    }
 }
diff --git a/server/src/main/java/org/opensearch/plugins/Plugin.java b/server/src/main/java/org/opensearch/plugins/Plugin.java
index 48486a6b55dfd..fd712be13cb73 100644
--- a/server/src/main/java/org/opensearch/plugins/Plugin.java
+++ b/server/src/main/java/org/opensearch/plugins/Plugin.java
@@ -269,4 +269,13 @@ public void close() throws IOException {
     public Collection<IndexSettingProvider> getAdditionalIndexSettingProviders() {
         return Collections.emptyList();
     }
+
+    /**
+     * Returns the {@link SecureSettingFactory} instance that could be used to configure the
+     * security related components (fe. transports)
+     * @return the {@link SecureSettingFactory} instance
+     */
+    public Optional<SecureSettingFactory> getSecureSettingFactory(Settings settings) {
+        return Optional.empty();
+    }
 }
diff --git a/server/src/main/java/org/opensearch/plugins/SecureSettingFactory.java b/server/src/main/java/org/opensearch/plugins/SecureSettingFactory.java
new file mode 100644
index 0000000000000..5682a8426a25b
--- /dev/null
+++ b/server/src/main/java/org/opensearch/plugins/SecureSettingFactory.java
@@ -0,0 +1,29 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.plugins;
+
+import org.opensearch.common.annotation.ExperimentalApi;
+import org.opensearch.common.settings.Settings;
+
+import java.util.Optional;
+
+/**
+ * A factory for creating the instance of the {@link SecureSettingProvider}, taking into account current settings.
+ *
+ * @opensearch.experimental
+ */
+@ExperimentalApi
+public interface SecureSettingFactory {
+    /**
+     * Creates (or provides pre-created) instance of the {@link SecureSettingProvider}
+     * @param settings settings
+     * @return optionally, the instance of the {@link SecureSettingProvider}
+     */
+    Optional<SecureSettingProvider> getOrCreate(Settings settings);
+}
diff --git a/server/src/main/java/org/opensearch/plugins/SecureSettingProvider.java b/server/src/main/java/org/opensearch/plugins/SecureSettingProvider.java
new file mode 100644
index 0000000000000..ae4b37f864845
--- /dev/null
+++ b/server/src/main/java/org/opensearch/plugins/SecureSettingProvider.java
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.plugins;
+
+import org.opensearch.common.annotation.ExperimentalApi;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.http.HttpServerTransport;
+import org.opensearch.transport.TcpTransport;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import java.util.Optional;
+
+/**
+ * A provider for security related settings.
+ *
+ * @opensearch.experimental
+ */
+@ExperimentalApi
+public interface SecureSettingProvider {
+    /**
+     * If supported, builds the {@link SSLEngine} instance for {@link HttpServerTransport} instance
+     * @param settings settings
+     * @param transport {@link HttpServerTransport} instance
+     * @return if supported, builds the {@link SSLEngine} instance
+     * @throws SSLException throws SSLException if the {@link SSLEngine} instance cannot be built
+     */
+    Optional<SSLEngine> buildSecureHttpEngine(Settings settings, HttpServerTransport transport) throws SSLException;
+
+    /**
+     * If supported, builds the {@link SSLEngine} instance for {@link TcpTransport} instance
+     * @param settings settings
+     * @param transport {@link TcpTransport} instance
+     * @return if supported, builds the {@link SSLEngine} instance
+     * @throws SSLException throws SSLException if the {@link SSLEngine} instance cannot be built
+     */
+    Optional<SSLEngine> buildSecureServerTransportEngine(Settings settings, TcpTransport transport) throws SSLException;
+
+    /**
+     * If supported, builds the {@link SSLEngine} instance for client transport instance
+     * @param settings settings
+     * @param hostname host name
+     * @param port port
+     * @return if supported, builds the {@link SSLEngine} instance
+     * @throws SSLException throws SSLException if the {@link SSLEngine} instance cannot be built
+     */
+    Optional<SSLEngine> buildSecureClientTransportEngine(Settings settings, String hostname, int port) throws SSLException;
+}