Skip to content

Commit f3b5299

Browse files
Move TransportGetLicenseAction to SAME Threadpool (#80993)
This is motivated by a number of recent SDHs that had these transport actions queue up on the manangement pool. These were not the reason for the blockage on the managment queue, but they are often sent at a high rate by Beats in the same scenarios that see a high rate of stats requests from Beats. Moving them off of the management pool at least makes sure that we don't get Beats retrying them over and over on slowness and generally saves some resources by avoiding ctx switches and having these requests live for longer than necessary. There's no point in running this on the management pool. It should have already been fast enough for SAME with the exception of reading the public key from disk maybe. Made it so the public key is just a constant and doesn't have to be read+deserialized over and over and also cached the verified property for a `License` instance so it should never have to be computed in practice anyway.
1 parent 7ce8054 commit f3b5299

File tree

9 files changed

+51
-30
lines changed

9 files changed

+51
-30
lines changed

x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.elasticsearch.common.cli.LoggingAwareCommand;
1818
import org.elasticsearch.core.PathUtils;
1919
import org.elasticsearch.core.SuppressForbidden;
20+
import org.elasticsearch.license.CryptUtils;
2021
import org.elasticsearch.license.License;
2122
import org.elasticsearch.license.LicenseVerifier;
2223
import org.elasticsearch.xcontent.ToXContent;
@@ -70,7 +71,7 @@ protected void execute(Terminal terminal, OptionSet options) throws Exception {
7071
}
7172

7273
// verify
73-
if (LicenseVerifier.verifyLicense(licenseSpec, Files.readAllBytes(publicKeyPath)) == false) {
74+
if (LicenseVerifier.verifyLicense(licenseSpec, CryptUtils.readPublicKey(Files.readAllBytes(publicKeyPath))) == false) {
7475
throw new UserException(ExitCodes.DATA_ERROR, "Invalid License!");
7576
}
7677
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);

x-pack/license-tools/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationTests.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.license.licensor;
88

99
import org.elasticsearch.core.TimeValue;
10+
import org.elasticsearch.license.CryptUtils;
1011
import org.elasticsearch.license.DateUtils;
1112
import org.elasticsearch.license.License;
1213
import org.elasticsearch.license.LicenseVerifier;
@@ -16,28 +17,32 @@
1617

1718
import java.nio.file.Files;
1819
import java.nio.file.Path;
20+
import java.security.PublicKey;
1921

2022
public class LicenseVerificationTests extends ESTestCase {
2123

2224
protected Path pubKeyPath = null;
25+
protected PublicKey publicKey;
2326
protected Path priKeyPath = null;
2427

2528
@Before
2629
public void setup() throws Exception {
2730
pubKeyPath = getDataPath("/public.key");
31+
publicKey = CryptUtils.readPublicKey(Files.readAllBytes(pubKeyPath));
2832
priKeyPath = getDataPath("/private.key");
2933
}
3034

3135
@After
3236
public void cleanUp() {
3337
pubKeyPath = null;
38+
publicKey = null;
3439
priKeyPath = null;
3540
}
3641

3742
public void testGeneratedLicenses() throws Exception {
3843
final TimeValue fortyEightHours = TimeValue.timeValueHours(2 * 24);
3944
final License license = TestUtils.generateSignedLicense(fortyEightHours, pubKeyPath, priKeyPath);
40-
assertTrue(LicenseVerifier.verifyLicense(license, Files.readAllBytes(pubKeyPath)));
45+
assertTrue(LicenseVerifier.verifyLicense(license, publicKey));
4146
}
4247

4348
public void testLicenseTampering() throws Exception {
@@ -50,15 +55,15 @@ public void testLicenseTampering() throws Exception {
5055
.validate()
5156
.build();
5257

53-
assertFalse(LicenseVerifier.verifyLicense(tamperedLicense, Files.readAllBytes(pubKeyPath)));
58+
assertFalse(LicenseVerifier.verifyLicense(tamperedLicense, publicKey));
5459
}
5560

5661
public void testRandomLicenseVerification() throws Exception {
5762
TestUtils.LicenseSpec licenseSpec = TestUtils.generateRandomLicenseSpec(
5863
randomIntBetween(License.VERSION_START, License.VERSION_CURRENT)
5964
);
6065
License generatedLicense = generateSignedLicense(licenseSpec, pubKeyPath, priKeyPath);
61-
assertTrue(LicenseVerifier.verifyLicense(generatedLicense, Files.readAllBytes(pubKeyPath)));
66+
assertTrue(LicenseVerifier.verifyLicense(generatedLicense, publicKey));
6267
}
6368

6469
private static License generateSignedLicense(TestUtils.LicenseSpec spec, Path pubKeyPath, Path priKeyPath) throws Exception {

x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,24 @@ private static void validateLimits(String type, int maxNodes, int maxResourceUni
471471
}
472472
}
473473

474+
private Boolean isVerified;
475+
476+
public boolean verified() {
477+
final Boolean v = isVerified;
478+
if (v != null) {
479+
return v;
480+
}
481+
final boolean verified = doVerify();
482+
this.isVerified = verified;
483+
return verified;
484+
}
485+
486+
private boolean doVerify() {
487+
boolean autoGeneratedLicense = License.isAutoGeneratedLicense(signature());
488+
return (autoGeneratedLicense && SelfGeneratedLicense.verify(this))
489+
|| (autoGeneratedLicense == false && LicenseVerifier.verifyLicense(this));
490+
}
491+
474492
public static License readLicense(StreamInput in) throws IOException {
475493
int version = in.readVInt(); // Version for future extensibility
476494
if (version > VERSION_CURRENT) {

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
import org.elasticsearch.common.settings.Setting;
2424
import org.elasticsearch.common.settings.Settings;
2525
import org.elasticsearch.common.time.DateFormatter;
26+
import org.elasticsearch.core.Nullable;
2627
import org.elasticsearch.core.TimeValue;
2728
import org.elasticsearch.env.Environment;
2829
import org.elasticsearch.gateway.GatewayService;
2930
import org.elasticsearch.protocol.xpack.XPackInfoResponse;
30-
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
3131
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
3232
import org.elasticsearch.protocol.xpack.license.PutLicenseResponse;
3333
import org.elasticsearch.threadpool.ThreadPool;
@@ -110,7 +110,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
110110
* Currently active license
111111
*/
112112
private final AtomicReference<License> currentLicense = new AtomicReference<>();
113-
private SchedulerEngine scheduler;
113+
private final SchedulerEngine scheduler;
114114
private final Clock clock;
115115

116116
/**
@@ -121,7 +121,7 @@ public class LicenseService extends AbstractLifecycleComponent implements Cluste
121121
/**
122122
* Callbacks to notify relative to license expiry
123123
*/
124-
private List<ExpirationCallback> expirationCallbacks = new ArrayList<>();
124+
private final List<ExpirationCallback> expirationCallbacks = new ArrayList<>();
125125

126126
/**
127127
* Which license types are permitted to be uploaded to the cluster
@@ -362,7 +362,7 @@ public void triggered(SchedulerEngine.Event event) {
362362
/**
363363
* Remove license from the cluster state metadata
364364
*/
365-
public void removeLicense(final DeleteLicenseRequest request, final ActionListener<PostStartBasicResponse> listener) {
365+
public void removeLicense(final ActionListener<PostStartBasicResponse> listener) {
366366
final PostStartBasicRequest startBasicRequest = new PostStartBasicRequest().acknowledge(true);
367367
clusterService.submitStateUpdateTask(
368368
"delete license",
@@ -609,15 +609,13 @@ public static License getLicense(final Metadata metadata) {
609609
return getLicense(licensesMetadata);
610610
}
611611

612-
static License getLicense(final LicensesMetadata metadata) {
612+
static License getLicense(@Nullable final LicensesMetadata metadata) {
613613
if (metadata != null) {
614614
License license = metadata.getLicense();
615615
if (license == LicensesMetadata.LICENSE_TOMBSTONE) {
616616
return license;
617617
} else if (license != null) {
618-
boolean autoGeneratedLicense = License.isAutoGeneratedLicense(license.signature());
619-
if ((autoGeneratedLicense && SelfGeneratedLicense.verify(license))
620-
|| (autoGeneratedLicense == false && LicenseVerifier.verifyLicense(license))) {
618+
if (license.verified()) {
621619
return license;
622620
}
623621
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseVerifier.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.ByteBuffer;
2222
import java.security.InvalidKeyException;
2323
import java.security.NoSuchAlgorithmException;
24+
import java.security.PublicKey;
2425
import java.security.Signature;
2526
import java.security.SignatureException;
2627
import java.util.Arrays;
@@ -38,7 +39,7 @@ public class LicenseVerifier {
3839
* @param license to verify
3940
* @return true if valid, false otherwise
4041
*/
41-
public static boolean verifyLicense(final License license, byte[] publicKeyData) {
42+
public static boolean verifyLicense(final License license, PublicKey publicKey) {
4243
byte[] signedContent = null;
4344
byte[] publicKeyFingerprint = null;
4445
try {
@@ -58,7 +59,7 @@ public static boolean verifyLicense(final License license, byte[] publicKeyData)
5859
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
5960
license.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(License.LICENSE_SPEC_VIEW_MODE, "true")));
6061
Signature rsa = Signature.getInstance("SHA512withRSA");
61-
rsa.initVerify(CryptUtils.readPublicKey(publicKeyData));
62+
rsa.initVerify(publicKey);
6263
BytesRefIterator iterator = BytesReference.bytes(contentBuilder).iterator();
6364
BytesRef ref;
6465
while ((ref = iterator.next()) != null) {
@@ -74,15 +75,19 @@ public static boolean verifyLicense(final License license, byte[] publicKeyData)
7475
}
7576
}
7677

77-
public static boolean verifyLicense(final License license) {
78-
final byte[] publicKeyBytes;
78+
private static final PublicKey PUBLIC_KEY;
79+
80+
static {
7981
try (InputStream is = LicenseVerifier.class.getResourceAsStream("/public.key")) {
8082
ByteArrayOutputStream out = new ByteArrayOutputStream();
8183
Streams.copy(is, out);
82-
publicKeyBytes = out.toByteArray();
83-
} catch (IOException ex) {
84-
throw new IllegalStateException(ex);
84+
PUBLIC_KEY = CryptUtils.readPublicKey(out.toByteArray());
85+
} catch (IOException e) {
86+
throw new AssertionError("key file is part of the source and must deserialize correctly", e);
8587
}
86-
return verifyLicense(license, publicKeyBytes);
88+
}
89+
90+
public static boolean verifyLicense(final License license) {
91+
return verifyLicense(license, PUBLIC_KEY);
8792
}
8893
}

x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensesMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class LicensesMetadata extends AbstractNamedDiffable<Metadata.Custom>
5252
.expiryDate(0)
5353
.build();
5454

55-
private License license;
55+
private final License license;
5656

5757
// This field describes the version of x-pack for which this cluster has exercised a trial. If the field
5858
// is null, then no trial has been exercised. We keep the version to leave open the possibility that we

x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportDeleteLicenseAction.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ protected void masterOperation(
6161
final ActionListener<AcknowledgedResponse> listener
6262
) throws ElasticsearchException {
6363
licenseService.removeLicense(
64-
request,
6564
listener.delegateFailure(
6665
(l, postStartBasicResponse) -> l.onResponse(AcknowledgedResponse.of(postStartBasicResponse.isAcknowledged()))
6766
)

x-pack/plugin/core/src/main/java/org/elasticsearch/license/TransportGetLicenseAction.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,10 @@
2323

2424
public class TransportGetLicenseAction extends TransportMasterNodeReadAction<GetLicenseRequest, GetLicenseResponse> {
2525

26-
private final LicenseService licenseService;
27-
2826
@Inject
2927
public TransportGetLicenseAction(
3028
TransportService transportService,
3129
ClusterService clusterService,
32-
LicenseService licenseService,
3330
ThreadPool threadPool,
3431
ActionFilters actionFilters,
3532
IndexNameExpressionResolver indexNameExpressionResolver
@@ -43,9 +40,8 @@ public TransportGetLicenseAction(
4340
GetLicenseRequest::new,
4441
indexNameExpressionResolver,
4542
GetLicenseResponse::new,
46-
ThreadPool.Names.MANAGEMENT
43+
ThreadPool.Names.SAME
4744
);
48-
this.licenseService = licenseService;
4945
}
5046

5147
@Override
@@ -60,6 +56,6 @@ protected void masterOperation(
6056
ClusterState state,
6157
final ActionListener<GetLicenseResponse> listener
6258
) throws ElasticsearchException {
63-
listener.onResponse(new GetLicenseResponse(licenseService.getLicense()));
59+
listener.onResponse(new GetLicenseResponse(LicenseService.getLicense(state.metadata())));
6460
}
6561
}

x-pack/plugin/core/src/test/java/org/elasticsearch/license/LicensesManagerServiceTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.elasticsearch.common.settings.Settings;
1212
import org.elasticsearch.core.TimeValue;
1313
import org.elasticsearch.plugins.Plugin;
14-
import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest;
1514
import org.elasticsearch.protocol.xpack.license.LicensesStatus;
1615
import org.elasticsearch.test.ESSingleNodeTestCase;
1716
import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin;
@@ -125,7 +124,7 @@ public void testRemoveLicenses() throws Exception {
125124
private void removeAndAckSignedLicenses(final LicenseService licenseService) {
126125
final CountDownLatch latch = new CountDownLatch(1);
127126
final AtomicBoolean success = new AtomicBoolean(false);
128-
licenseService.removeLicense(new DeleteLicenseRequest(), new ActionListener<PostStartBasicResponse>() {
127+
licenseService.removeLicense(new ActionListener<PostStartBasicResponse>() {
129128
@Override
130129
public void onResponse(PostStartBasicResponse postStartBasicResponse) {
131130
if (postStartBasicResponse.isAcknowledged()) {

0 commit comments

Comments
 (0)