Skip to content

Commit 20ca640

Browse files
committed
Add signing configuration for cross cluster api keys
1 parent 85f7fa9 commit 20ca640

File tree

18 files changed

+1560
-17
lines changed

18 files changed

+1560
-17
lines changed

server/src/main/java/org/elasticsearch/transport/RemoteClusterSettings.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@
4242

4343
public class RemoteClusterSettings {
4444

45+
public static final String REMOTE_CLUSTER_SETTINGS_PREFIX = "cluster.remote.";
46+
4547
public static final TimeValue DEFAULT_INITIAL_CONNECTION_TIMEOUT = TimeValue.timeValueSeconds(30);
4648
/**
4749
* The initial connect timeout for remote cluster connections
4850
*/
4951
public static final Setting<TimeValue> REMOTE_INITIAL_CONNECTION_TIMEOUT_SETTING = Setting.positiveTimeSetting(
50-
"cluster.remote.initial_connect_timeout",
52+
REMOTE_CLUSTER_SETTINGS_PREFIX + "initial_connect_timeout",
5153
DEFAULT_INITIAL_CONNECTION_TIMEOUT,
5254
Setting.Property.NodeScope
5355
);
@@ -59,13 +61,13 @@ public class RemoteClusterSettings {
5961
* The value of the setting is expected to be a boolean, {@code true} for nodes that can become gateways, {@code false} otherwise.
6062
*/
6163
public static final Setting<String> REMOTE_NODE_ATTRIBUTE = Setting.simpleString(
62-
"cluster.remote.node.attr",
64+
REMOTE_CLUSTER_SETTINGS_PREFIX + "node.attr",
6365
Setting.Property.NodeScope
6466
);
6567

6668
public static final boolean DEFAULT_SKIP_UNAVAILABLE = true;
6769
public static final Setting.AffixSetting<Boolean> REMOTE_CLUSTER_SKIP_UNAVAILABLE = Setting.affixKeySetting(
68-
"cluster.remote.",
70+
REMOTE_CLUSTER_SETTINGS_PREFIX,
6971
"skip_unavailable",
7072
(ns, key) -> boolSetting(
7173
key,
@@ -77,7 +79,7 @@ public class RemoteClusterSettings {
7779
);
7880

7981
public static final Setting.AffixSetting<TimeValue> REMOTE_CLUSTER_PING_SCHEDULE = Setting.affixKeySetting(
80-
"cluster.remote.",
82+
REMOTE_CLUSTER_SETTINGS_PREFIX,
8183
"transport.ping_schedule",
8284
(ns, key) -> timeSetting(
8385
key,
@@ -89,7 +91,7 @@ public class RemoteClusterSettings {
8991
);
9092

9193
public static final Setting.AffixSetting<Compression.Enabled> REMOTE_CLUSTER_COMPRESS = Setting.affixKeySetting(
92-
"cluster.remote.",
94+
REMOTE_CLUSTER_SETTINGS_PREFIX,
9395
"transport.compress",
9496
(ns, key) -> enumSetting(
9597
Compression.Enabled.class,
@@ -102,7 +104,7 @@ public class RemoteClusterSettings {
102104
);
103105

104106
public static final Setting.AffixSetting<Compression.Scheme> REMOTE_CLUSTER_COMPRESSION_SCHEME = Setting.affixKeySetting(
105-
"cluster.remote.",
107+
REMOTE_CLUSTER_SETTINGS_PREFIX,
106108
"transport.compression_scheme",
107109
(ns, key) -> enumSetting(
108110
Compression.Scheme.class,
@@ -115,7 +117,7 @@ public class RemoteClusterSettings {
115117
);
116118

117119
public static final Setting.AffixSetting<SecureString> REMOTE_CLUSTER_CREDENTIALS = Setting.affixKeySetting(
118-
"cluster.remote.",
120+
REMOTE_CLUSTER_SETTINGS_PREFIX,
119121
"credentials",
120122
key -> SecureSetting.secureString(key, null)
121123
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.security.transport;
9+
10+
import org.elasticsearch.common.settings.MockSecureSettings;
11+
import org.elasticsearch.common.settings.Settings;
12+
import org.elasticsearch.common.ssl.PemKeyConfig;
13+
import org.elasticsearch.test.SecurityIntegTestCase;
14+
15+
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_CERT_PATH;
16+
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_ALIAS;
17+
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_PATH;
18+
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEYSTORE_SECURE_PASSWORD;
19+
import static org.elasticsearch.xpack.security.transport.CrossClusterApiKeySignerSettings.SIGNING_KEY_PATH;
20+
21+
public class CrossClusterApiKeySignerIntegTests extends SecurityIntegTestCase {
22+
23+
private static final String DYNAMIC_TEST_CLUSTER_ALIAS = "dynamic_test_cluster";
24+
private static final String STATIC_TEST_CLUSTER_ALIAS = "static_test_cluster";
25+
26+
public void testSignWithPemKeyConfig() {
27+
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
28+
CrossClusterApiKeySigner.class,
29+
internalCluster().getRandomNodeName()
30+
);
31+
final String[] testHeaders = randomArray(5, String[]::new, () -> randomAlphanumericOfLength(randomInt(20)));
32+
33+
X509CertificateSignature signature = signer.sign(STATIC_TEST_CLUSTER_ALIAS, testHeaders);
34+
signature.certificate().getPublicKey();
35+
36+
var keyConfig = new PemKeyConfig(
37+
"signing_rsa.crt",
38+
"signing_rsa.key",
39+
new char[0],
40+
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.crt").getParent()
41+
);
42+
43+
assertEquals(signature.algorithm(), keyConfig.getKeys().getFirst().v2().getSigAlgName());
44+
assertEquals(signature.certificate(), keyConfig.getKeys().getFirst().v2());
45+
}
46+
47+
public void testSignUnknownClusterAlias() {
48+
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
49+
CrossClusterApiKeySigner.class,
50+
internalCluster().getRandomNodeName()
51+
);
52+
final String[] testHeaders = randomArray(5, String[]::new, () -> randomAlphanumericOfLength(randomInt(20)));
53+
54+
X509CertificateSignature signature = signer.sign("unknowncluster", testHeaders);
55+
assertNull(signature);
56+
}
57+
58+
public void testSeveralKeyStoreAliases() {
59+
final CrossClusterApiKeySigner signer = internalCluster().getInstance(
60+
CrossClusterApiKeySigner.class,
61+
internalCluster().getRandomNodeName()
62+
);
63+
64+
try {
65+
// Create a new config without an alias. Since there are several aliases in the keystore, no signature should be generated
66+
updateClusterSettings(
67+
Settings.builder()
68+
.put(
69+
SIGNING_KEYSTORE_PATH.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(),
70+
getDataPath("/org/elasticsearch/xpack/security/signature/signing.jks")
71+
)
72+
);
73+
74+
{
75+
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
76+
assertNull(signature);
77+
}
78+
79+
// Add an alias from the keystore
80+
updateClusterSettings(
81+
Settings.builder()
82+
.put(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(), "wholelottakey")
83+
);
84+
{
85+
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
86+
assertNotNull(signature);
87+
}
88+
89+
// Add an alias not in the keystore
90+
updateClusterSettings(
91+
Settings.builder()
92+
.put(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(), "idonotexist")
93+
);
94+
{
95+
X509CertificateSignature signature = signer.sign(DYNAMIC_TEST_CLUSTER_ALIAS, "test", "test");
96+
assertNull(signature);
97+
}
98+
} finally {
99+
updateClusterSettings(
100+
Settings.builder()
101+
.putNull(SIGNING_KEYSTORE_PATH.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey())
102+
.putNull(SIGNING_KEYSTORE_ALIAS.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey())
103+
.setSecureSettings(new MockSecureSettings())
104+
);
105+
}
106+
}
107+
108+
@Override
109+
protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) {
110+
var builder = Settings.builder();
111+
MockSecureSettings secureSettings = (MockSecureSettings) builder.put(super.nodeSettings(nodeOrdinal, otherSettings))
112+
.put(
113+
SIGNING_CERT_PATH.getConcreteSettingForNamespace(STATIC_TEST_CLUSTER_ALIAS).getKey(),
114+
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.crt")
115+
)
116+
.put(
117+
SIGNING_KEY_PATH.getConcreteSettingForNamespace(STATIC_TEST_CLUSTER_ALIAS).getKey(),
118+
getDataPath("/org/elasticsearch/xpack/security/signature/signing_rsa.key")
119+
)
120+
.getSecureSettings();
121+
secureSettings.setString(
122+
SIGNING_KEYSTORE_SECURE_PASSWORD.getConcreteSettingForNamespace(DYNAMIC_TEST_CLUSTER_ALIAS).getKey(),
123+
"secretpassword"
124+
);
125+
return builder.build();
126+
}
127+
}

0 commit comments

Comments
 (0)