Skip to content

KNOX-3359 - Support Single-Purpose EKU Certificates#1291

Open
moresandeep wants to merge 16 commits into
apache:masterfrom
moresandeep:KNOX-3359
Open

KNOX-3359 - Support Single-Purpose EKU Certificates#1291
moresandeep wants to merge 16 commits into
apache:masterfrom
moresandeep:KNOX-3359

Conversation

@moresandeep

@moresandeep moresandeep commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

(It is very important that you created an Apache Knox JIRA for this change and that the PR title/commit message includes the Apache Knox JIRA ID!)

KNOX-3359 - Support Single-Purpose EKU Certificates

Public CAs are retiring dual purpose certificates that carry both serverAuth and clientAuth Extended Key Usages. This change allows Knox to use two separate keystores, one for its inbound TLS server identity (serverAuth only) and one for its outbound mTLS client identity (clientAuth only).

What changes were proposed in this pull request?

  • gateway.tls.single.eku.enabled — new toggle that activates single-EKU mode. Off by default all existing behavior is unchanged when it is off.
  • GatewayConfigImpl / GatewayConfig — new config accessors for the HTTP client keystore path, type, alias, and truststore, startup validation (validateSingleEkuConfig) that fails fast if any prerequisite is missing or misconfigured (wrong alias type, wrong EKU, missing truststore). Knox refuses to start rather than silently falling back to the server identity.
  • DefaultKeystoreService.getKeystoreForHttpClient() / DefaultAliasService.getHttpClientKeyPassphrase() — load the dedicated client-identity keystore using the configured path, type, and alias.
  • DefaultHttpClientFactory.createSSLContext() — when single-EKU is on and two-way SSL is active, loads the client keystore instead of the gateway server keystore.
  • JettySSLService - enforce that the server keystore alias carries only serverAuth in single-EKU mode.
  • gateway.httpclient.twoWaySsl.enabled — new global flag to activate outbound mTLS across all dispatches without requiring use-two-way-ssl="true" on every service definition. Automatically defaults to true when gateway.tls.single.eku.enabled=true, so enabling single-EKU mode is a single config change with sane defaults.

Notes

Note that this feature is independent of mTLS. mTLS can be configured using the existing properties

  • gateway.client.auth.needed | default false | comments: INBOUND mTLS
  • gateway.httpclient.twoWaySsl.enabled | default false | comments: OUTBOUND mTLS

How was this patch tested?

Unit Tests

  • Unit tests added for GatewayConfigImpl, DefaultKeystoreService, DefaultAliasService, JettySSLService, and DefaultHttpClientFactory covering the single-EKU happy path, wrong-passphrase fail-closed, one-way SSL guard (no client cert presented), and the global flag OR-ing with the per-dispatch parameter.

Manual Tests

Tested the following scenarios

Happy path: clientAuth cert + basic auth → 200, backend sees knox-client

curl -sk --cert curl-client.crt --key curl-client.key -u guest:guest-password https://localhost:8443/gateway/sandbox/testmtls/hello
{"service":"test-https-backend","path":"/hello?user.name=guest","authenticated_client_cn":"knox-client","message":"mTLS OK -- backend saw your client certificate"}

No client cert → inbound TLS handshake rejected (client.auth.needed=true)

curl -iku guest:guest-password https://localhost:8443/gateway/sandbox/testmtls/hello-
curl: (56) LibreSSL SSL_read: LibreSSL/3.3.6: error:1404C412:SSL 
routines:ST_OK:sslv3 alert bad certificate, errno 0

serverAuth-ONLY cert presented as inbound client cert → rejected (EKU check)

curl -ik --cert knox-server.crt --key knox-server.key -u guest:guest-password https://localhost:8443/gateway/sandbox/testmtls/hello
curl: (55) Send failure: Broken pipe

Valid client cert but NO credentials → TLS ok, HTTP 401 from Knox'

curl -ik --cert curl-client.crt --key curl-client.key https://localhost:8443/gateway/sandbox/testmtls/hello
HTTP/1.1 401 Unauthorized
Date: Wed, 01 Jul 2026 18:56:23 GMT
WWW-Authenticate: BASIC realm="application"
Content-Length: 0

Integration Tests

Added new integration tests

UI changes

NA

@moresandeep moresandeep requested review from hanicz and smolnar82 and removed request for smolnar82 July 1, 2026 19:27
@moresandeep moresandeep self-assigned this Jul 1, 2026
@smolnar82 smolnar82 self-requested a review July 2, 2026 07:30
Design for separate client/server keystores and truststores to support
single-purpose (single-EKU) certificates, gated behind an explicit
gateway.tls.single.eku.enabled toggle with fail-fast startup validation.

# Conflicts:
#	gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/AliasBasedTokenStateServiceTest.java
…ulting to true when single-EKU is on

wire gateway.httpclient.twoWaySsl.enabled into DefaultHttpClientFactory; update log message
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Test Results

36 tests   36 ✅  5s ⏱️
 3 suites   0 💤
 3 files     0 ❌

Results for commit 6945815.

♻️ This comment has been updated with latest results.

@moresandeep moresandeep requested a review from pzampino July 7, 2026 14:52
public boolean isHttpClientTwoWaySslEnabled() {
String configured = get(HTTP_CLIENT_TWO_WAY_SSL_ENABLED);
if (configured != null) {
return Boolean.parseBoolean(configured);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe parseBoolean returns false for null, so I think you could eliminate the null check unless this method intends to fall back to isSingleEkuEnabled() IFF two-way SSL is NOT explicitly configured. Is that the intent here? If so, this only applies to HTTP clients employed by Knox?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this method intends to fall back to isSingleEkuEnabled() this is because, for single EKU feature to work two-way-ssl is required as a result if two-way-ssl is not configured we default it to isSingleEkuEnabled().
So, if someone turned on isSingleEkuEnabled() then they not need to specifically turn on two-way-ssl as those go hand in hand.

// 1. Outbound client-identity keystore must be configured and contain the configured key entry.
String clientKeystorePath = config.getHttpClientKeystorePath();
if (clientKeystorePath == null || clientKeystorePath.isEmpty()) {
throw new ServiceLifecycleException("Single-EKU mode is enabled (" + GatewayConfig.TLS_SINGLE_EKU_ENABLED

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it not valid to have single-purpose certs enabled WITHOUT two-way SSL, thus not requiring the client identity keystore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

single-purpose certs need to have two-way SSL, this was pointed out by @smolnar82 in his offline review

This means Knox in "single-EKU mode" will start happily with a client keystore holding a serverAuth-only or dual-purpose cert — exactly the misconfiguration this feature exists to catch. The validation gives a false sense of assurance, and the tests reflect the gap (JettySSLServiceTest covers path-missing, truststore-missing, client-auth-disabled, and cert-not-a-key, but there is no EKU test).


// 2. Inbound client authentication must be enforced (server truststore + client-auth).
if (config.getTruststorePath() == null) {
throw new ServiceLifecycleException("Single-EKU mode is enabled but the server truststore ("

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Again, is it not permissible to have single-purpose certs enabled for Knox without assuming/requireing two-way SSL?

@moresandeep moresandeep Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct, two-way-SSL is required for this feature. But this change specifically ensures that we have truststore configured for clients who will talk to us over mTLS.

throw new ServiceLifecycleException("Single-EKU mode is enabled but the server truststore ("
+ GatewayConfig.GATEWAY_TRUSTSTORE_PATH + ") is not configured. Server will not start.");
}
if (!config.isClientAuthNeeded() && !config.isClientAuthWanted()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here too it seems two-way SSL is required just because single-purpose certs are enabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think support for single-purpose certs necessitates mTLS; I think they are orthogonal concerns, but I may be wrong.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You might be right, let me check.

@moresandeep moresandeep requested a review from pzampino July 8, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants