Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
20ca640
Add signing configuration for cross cluster api keys
jfreden Aug 28, 2025
4fea9e2
Update docs/changelog/134137.yaml
jfreden Sep 5, 2025
93f2304
fixup! CI
jfreden Sep 8, 2025
4035b14
fixup! fips
jfreden Sep 8, 2025
ab97104
fixup! Update forbidden patterns
jfreden Sep 8, 2025
181b715
fixup! Test issue
jfreden Sep 8, 2025
75e6ac2
fixup! Update tests for fips compliance
jfreden Sep 8, 2025
648892e
Merge remote-tracking branch 'upstream/main' into rcs2.1/conf_signing…
jfreden Sep 8, 2025
1fe6444
fixup! Make buildEffectiveSettings clearer
jfreden Sep 9, 2025
b87acda
fixup! Code review comments
jfreden Sep 9, 2025
8e24ec6
[CI] Auto commit changes from spotless
Sep 9, 2025
3ce1bf6
fixup! address race condition when creating signer
jfreden Sep 9, 2025
e500b0f
fixup! Do not include private key in exception
jfreden Sep 9, 2025
c9c0b73
[CI] Auto commit changes from spotless
Sep 9, 2025
effcde7
fixup! Strict validation on node startup
jfreden Sep 10, 2025
2b48116
Merge remote-tracking branch 'upstream/main' into rcs2.1/conf_signing…
jfreden Sep 10, 2025
8f3f4c6
[CI] Auto commit changes from spotless
Sep 10, 2025
57641b5
Merge branch 'main' into rcs2.1/conf_signing_cert
jfreden Sep 10, 2025
558ad02
fixup! handle race condition and concurrency bug
jfreden Sep 11, 2025
04257b7
fixup! Simplify after refactor
jfreden Sep 11, 2025
fad4c41
Simplify further
jfreden Sep 11, 2025
67a6287
Merge remote-tracking branch 'upstream/main' into rcs2.1/conf_signing…
jfreden Sep 11, 2025
8077f01
fixup! Use new secure settings util
jfreden Sep 11, 2025
1347761
fixup! Config corner case
jfreden Sep 11, 2025
727bc38
Add support for validator in addAffixGroupUpdateConsumer
jfreden Sep 15, 2025
2e178ba
Use validator
jfreden Sep 15, 2025
00eb36f
fixup! Wrong api...
jfreden Sep 15, 2025
5a9af92
Merge remote-tracking branch 'upstream/main' into rcs2.1/conf_signing…
jfreden Sep 15, 2025
d6a582b
[CI] Auto commit changes from spotless
Sep 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@

public class RemoteClusterSettings {

public static final String REMOTE_CLUSTER_SETTINGS_PREFIX = "cluster.remote.";

public static final TimeValue DEFAULT_INITIAL_CONNECTION_TIMEOUT = TimeValue.timeValueSeconds(30);
/**
* The initial connect timeout for remote cluster connections
*/
public static final Setting<TimeValue> REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING = Setting.positiveTimeSetting(
"cluster.remote.initial_connect_timeout",
REMOTE_CLUSTER_SETTINGS_PREFIX + "initial_connect_timeout",
DEFAULT_INITIAL_CONNECTION_TIMEOUT,
Setting.Property.NodeScope
);
Expand All @@ -59,13 +61,13 @@ public class RemoteClusterSettings {
* The value of the setting is expected to be a boolean, {@code true} for nodes that can become gateways, {@code false} otherwise.
*/
public static final Setting<String> REMOTE_NODE_ATTRIBUTE = Setting.simpleString(
"cluster.remote.node.attr",
REMOTE_CLUSTER_SETTINGS_PREFIX + "node.attr",
Setting.Property.NodeScope
);

public static final boolean DEFAULT_SKIP_UNAVAILABLE = true;
public static final Setting.AffixSetting<Boolean> REMOTE_CLUSTER_SKIP_UNAVAILABLE = Setting.affixKeySetting(
"cluster.remote.",
REMOTE_CLUSTER_SETTINGS_PREFIX,
"skip_unavailable",
(ns, key) -> boolSetting(
key,
Expand All @@ -77,7 +79,7 @@ public class RemoteClusterSettings {
);

public static final Setting.AffixSetting<TimeValue> REMOTE_CLUSTER_PING_SCHEDULE = Setting.affixKeySetting(
"cluster.remote.",
REMOTE_CLUSTER_SETTINGS_PREFIX,
"transport.ping_schedule",
(ns, key) -> timeSetting(
key,
Expand All @@ -89,7 +91,7 @@ public class RemoteClusterSettings {
);

public static final Setting.AffixSetting<Compression.Enabled> REMOTE_CLUSTER_COMPRESS = Setting.affixKeySetting(
"cluster.remote.",
REMOTE_CLUSTER_SETTINGS_PREFIX,
"transport.compress",
(ns, key) -> enumSetting(
Compression.Enabled.class,
Expand All @@ -102,7 +104,7 @@ public class RemoteClusterSettings {
);

public static final Setting.AffixSetting<Compression.Scheme> REMOTE_CLUSTER_COMPRESSION_SCHEME = Setting.affixKeySetting(
"cluster.remote.",
REMOTE_CLUSTER_SETTINGS_PREFIX,
"transport.compression_scheme",
(ns, key) -> enumSetting(
Compression.Scheme.class,
Expand All @@ -115,7 +117,7 @@ public class RemoteClusterSettings {
);

public static final Setting.AffixSetting<SecureString> REMOTE_CLUSTER_CREDENTIALS = Setting.affixKeySetting(
"cluster.remote.",
REMOTE_CLUSTER_SETTINGS_PREFIX,
"credentials",
key -> SecureSetting.secureString(key, null)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.security.transport;

import org.elasticsearch.common.settings.MockSecureSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.ssl.PemKeyConfig;
import org.elasticsearch.test.SecurityIntegTestCase;

import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_CERT_PATH;
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_ALIAS;
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_PATH;
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_SECURE_PASSWORD;
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEY_PATH;

public class CrossClusterApiKeySignerIntegTests extends SecurityIntegTestCase {

private static final String DYNAMIC_TEST_CLUSTER_ALIAS = "dynamic_test_cluster";
private static final String STATIC_TEST_CLUSTER_ALIAS = "static_test_cluster";

public void testSignWithPemKeyConfig() {
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
CrossClusterApiKeySigner.class,
internalCluster().getRandomNodeName()
);
final String[] testHeaders = randomArray(5, String[]::new, () -> randomAlphanumericOfLength(randomInt(20)));

X509CertificateSignature signature = signer.sign(STATIC_TEST_CLUSTER_ALIAS, testHeaders);
signature.certificate().getPublicKey();

var keyConfig = new PemKeyConfig(
"signing_rsa.crt",
"signing_rsa.key",
new char[0],
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.crt").getParent()
);

assertEquals(signature.algorithm(), keyConfig.getKeys().getFirst().v2().getSigAlgName());
assertEquals(signature.certificate(), keyConfig.getKeys().getFirst().v2());
}

public void testSignUnknownClusterAlias() {
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
CrossClusterApiKeySigner.class,
internalCluster().getRandomNodeName()
);
final String[] testHeaders = randomArray(5, String[]::new, () -> randomAlphanumericOfLength(randomInt(20)));

X509CertificateSignature signature = signer.sign("unknowncluster", testHeaders);
assertNull(signature);
}

public void testSeveralKeyStoreAliases() {
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
CrossClusterApiKeySigner.class,
internalCluster().getRandomNodeName()
);

try {
// Create a new config without an alias. Since there are several aliases in the keystore, no signature should be generated
updateClusterSettings(
Settings.builder()
.put(
SIGNING_KEYSTORE_PATH.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(),
getDataPath("/org/elasticsearch/xpack/security/signature/signing.jks")
)
);

{
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
assertNull(signature);
}

// Add an alias from the keystore
updateClusterSettings(
Settings.builder()
.put(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(), "wholelottakey")
);
{
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
assertNotNull(signature);
}

// Add an alias not in the keystore
updateClusterSettings(
Settings.builder()
.put(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(), "idonotexist")
);
{
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
assertNull(signature);
}
} finally {
updateClusterSettings(
Settings.builder()
.putNull(SIGNING_KEYSTORE_PATH.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey())
.putNull(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey())
.setSecureSettings(new MockSecureSettings())
);
}
}

@Override
protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) {
var builder = Settings.builder();
MockSecureSettings secureSettings = (MockSecureSettings) builder.put(super.nodeSettings(nodeOrdinal, otherSettings))
.put(
SIGNING_CERT_PATH.getConcreteSettingForNamespace(STATIC_TEST_CLUSTER_ALIAS).getKey(),
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.crt")
)
.put(
SIGNING_KEY_PATH.getConcreteSettingForNamespace(STATIC_TEST_CLUSTER_ALIAS).getKey(),
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.key")
)
.getSecureSettings();
secureSettings.setString(
SIGNING_KEYSTORE_SECURE_PASSWORD.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(),
"secretpassword"
);
return builder.build();
}
}
Loading