Skip to content

Add mutual_tls authentication plugin for HTTP-based sources#6916

Open
divakarsingh wants to merge 1 commit into
opensearch-project:mainfrom
divakarsingh:feature/mtls-client-auth
Open

Add mutual_tls authentication plugin for HTTP-based sources#6916
divakarsingh wants to merge 1 commit into
opensearch-project:mainfrom
divakarsingh:feature/mtls-client-auth

Conversation

@divakarsingh

Copy link
Copy Markdown

Description

Adds a mutual_tls authentication plugin that enables mTLS client certificate
authentication on all HTTP-based sources. Clients must present a valid certificate
signed by the configured trust CA to connect.

This is implemented as an ArmeriaHttpAuthenticationProvider plugin (consistent with
http_basic and unauthenticated), with a new getTlsCustomizer() method on the
interface for TLS-layer configuration.

Configuration example:

source:
  opensearch_api:
    ssl: true
    ssl_certificate_file: "/certs/server.crt"
    ssl_key_file: "/certs/server.key"
    authentication:
      mutual_tls:
        ssl_trust_certificate_file: "/certs/ca.crt"

Works on all HTTP-based sources: http, opensearch_api, otel_trace_source,
otel_metrics_source, otel_logs_source.

Issues Resolved

Resolves #6889

Check List

  • New functionality includes testing (unit + integration)
  • Commits are signed with DCO (Signed-off-by)

Add a new ArmeriaHttpAuthenticationProvider plugin called mutual_tls
that configures Armeria with ClientAuth.REQUIRE and a trust CA for
client certificate verification at the TLS handshake level.

Extends the ArmeriaHttpAuthenticationProvider interface with a
getTlsCustomizer() method so authentication plugins can configure
TLS-level settings. BaseHttpSource applies this during server setup.

This applies to all HTTP-based sources (http, opensearch_api,
otel_trace_source, otel_metrics_source, otel_logs_source) since they
all inherit from BaseHttpSource.

Configuration:
  authentication:
    mutual_tls:
      ssl_trust_certificate_file: /path/to/ca.crt

Resolves opensearch-project#6889

Signed-off-by: Divakar Pratap Singh <divakar.p.singh@gmail.com>

@srikanthpadakanti srikanthpadakanti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @divakarsingh Thank you for your contribution. Can you please look into these comments.

new ByteArrayInputStream(certificate.getPrivateKey().getBytes(StandardCharsets.UTF_8)
)
);
authenticationProvider.getTlsCustomizer().ifPresent(sb::tlsCustomizer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTlsCustomizer is applied only inside if (sourceConfig.isSsl()); if user configures mutual_tls without ssl: true, server starts unauthenticated with no warning, silent fail-open

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unencrypted private keys committed to repo. Test PEMs must be generated at runtime via BouncyCastle PEMParser, not committed.

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unencrypted private keys committed to repo. Test PEMs must be generated at runtime via BouncyCastle PEMParser, not committed.

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unencrypted private keys committed to repo. Test PEMs must be generated at runtime via BouncyCastle PEMParser, not committed.

public class MutualTlsAuthenticationConfig {

@JsonProperty("ssl_trust_certificate_file")
@NotBlank(message = "ssl_trust_certificate_file is required for mutual_tls authentication")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only @notblank validation. No check that the file exists, is readable, or parses as an X.509 CA cert. Failure surfaces as an SSL exception at server start instead of a clear config-time validation error.

public class MutualTlsAuthenticationConfig {

@JsonProperty("ssl_trust_certificate_file")
@NotBlank(message = "ssl_trust_certificate_file is required for mutual_tls authentication")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only a file path is accepted. Inline PEM content (used elsewhere via http-client-common) is not supported. Inconsistent UX across auth configs

public class MutualTlsAuthenticationConfig {

@JsonProperty("ssl_trust_certificate_file")
@NotBlank(message = "ssl_trust_certificate_file is required for mutual_tls authentication")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

single trust cert file only. No list, no multiple CAs. Multi-tenant or staging-plus-prod deployments cannot use this as-is.

@Override
public Optional<Consumer<SslContextBuilder>> getTlsCustomizer() {
return Optional.of(sslContextBuilder -> {
sslContextBuilder.clientAuth(ClientAuth.REQUIRE);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no CRL or OCSP wiring. mTLS without revocation does not survive a leaked client cert. At minimum, add a follow-up issue and document the gap.

* Called during server setup when SSL is enabled.
*
* @return an optional consumer that configures the {@link SslContextBuilder}
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTlsCustomizer returns Consumer<SslContextBuilder>, giving any plugin full mutation rights over the SslContext (server key, server cert, protocols). A misbehaving plugin can downgrade or break TLS. Narrow the contract (e.g., a small interface that only exposes trustManager and clientAuth setters).

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No logging or metric on TLS handshake rejection. An operator cannot distinguish a network blip from a cert-rejection from a misconfigured client. Add a counter or a structured log emit on handshake failure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add mTLS client certificate authentication to HTTP-based sources

2 participants