-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve security-crypto threadpool overflow handling #111369
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8448162
Add unit test reproducing crypto threadpool queue overflow
gwbrown c95c9fb
Fix cache issues on crypto threadpool overflow
gwbrown ca31b26
Update docs/changelog/111369.yaml
gwbrown 5bfa31a
Cleanup per review feedback, thanks Dave
gwbrown 5623f62
Merge remote-tracking branch 'origin/main' into api-key-cache
gwbrown bb9141d
Merge remote-tracking branch 'gwbrown/api-key-cache' into api-key-cache
gwbrown 875fae1
Merge remote-tracking branch 'origin/main' into api-key-cache
gwbrown File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
pr: 111369 | ||
summary: Improve security-crypto threadpool overflow handling | ||
area: Authentication | ||
type: bug | ||
issues: [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,6 +146,7 @@ | |
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.CyclicBarrier; | ||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Semaphore; | ||
|
@@ -230,6 +231,9 @@ public class ApiKeyServiceTests extends ESTestCase { | |
"search": [ {"names": ["logs"]} ], | ||
"replication": [ {"names": ["archive"]} ] | ||
}"""); | ||
|
||
private static final int TEST_THREADPOOL_QUEUE_SIZE = 1000; | ||
|
||
private ThreadPool threadPool; | ||
private Client client; | ||
private SecurityIndexManager securityIndex; | ||
|
@@ -245,7 +249,7 @@ public void createThreadPool() { | |
Settings.EMPTY, | ||
SECURITY_CRYPTO_THREAD_POOL_NAME, | ||
1, | ||
1000, | ||
TEST_THREADPOOL_QUEUE_SIZE, | ||
"xpack.security.crypto.thread_pool", | ||
EsExecutors.TaskTrackingConfig.DO_NOT_TRACK | ||
) | ||
|
@@ -268,6 +272,90 @@ public void setupMocks() { | |
doAnswer(invocation -> Instant.now()).when(clock).instant(); | ||
} | ||
|
||
public void testFloodThreadpool() throws Exception { | ||
// We're going to be blocking the security-crypto threadpool so we need a new one for the client | ||
ThreadPool clientThreadpool = new TestThreadPool( | ||
this.getTestName(), | ||
new FixedExecutorBuilder( | ||
Settings.EMPTY, | ||
this.getTestName(), | ||
1, | ||
100, | ||
"no_settings_used", | ||
EsExecutors.TaskTrackingConfig.DO_NOT_TRACK | ||
) | ||
); | ||
try { | ||
when(client.threadPool()).thenReturn(clientThreadpool); | ||
|
||
// setup copied from testAuthenticateWithApiKey | ||
final Settings settings = Settings.builder().put(XPackSettings.API_KEY_SERVICE_ENABLED_SETTING.getKey(), true).build(); | ||
final ApiKeyService service = createApiKeyService(settings); | ||
|
||
final String id = randomAlphaOfLength(12); | ||
final String key = randomAlphaOfLength(16); | ||
|
||
final User user, authUser; | ||
if (randomBoolean()) { | ||
user = new User("hulk", new String[] { "superuser" }, "Bruce Banner", "[email protected]", Map.of(), true); | ||
authUser = new User("authenticated_user", "other"); | ||
} else { | ||
user = new User("hulk", new String[] { "superuser" }, "Bruce Banner", "[email protected]", Map.of(), true); | ||
authUser = null; | ||
} | ||
final ApiKey.Type type = randomFrom(ApiKey.Type.values()); | ||
final Map<String, Object> metadata = mockKeyDocument(id, key, user, authUser, false, Duration.ofSeconds(3600), null, type); | ||
|
||
// Block the security crypto threadpool | ||
CyclicBarrier barrier = new CyclicBarrier(2); | ||
threadPool.executor(SECURITY_CRYPTO_THREAD_POOL_NAME).execute(() -> safeAwait(barrier)); | ||
// Now fill it up while the one thread is blocked | ||
for (int i = 0; i < TEST_THREADPOOL_QUEUE_SIZE; i++) { | ||
threadPool.executor(SECURITY_CRYPTO_THREAD_POOL_NAME).execute(() -> {}); | ||
} | ||
|
||
// Check that it's full | ||
for (var stat : threadPool.stats().stats()) { | ||
if (stat.name().equals(SECURITY_CRYPTO_THREAD_POOL_NAME)) { | ||
assertThat(stat.queue(), equalTo(TEST_THREADPOOL_QUEUE_SIZE)); | ||
assertThat(stat.rejected(), equalTo(0L)); | ||
} | ||
} | ||
|
||
// now try to auth with an API key | ||
final AuthenticationResult<User> auth = tryAuthenticate(service, id, key, type); | ||
assertThat(auth.getStatus(), is(AuthenticationResult.Status.TERMINATE)); | ||
|
||
// Make sure one was rejected and the queue is still full | ||
for (var stat : threadPool.stats().stats()) { | ||
if (stat.name().equals(SECURITY_CRYPTO_THREAD_POOL_NAME)) { | ||
assertThat(stat.queue(), equalTo(TEST_THREADPOOL_QUEUE_SIZE)); | ||
assertThat(stat.rejected(), equalTo(1L)); | ||
} | ||
} | ||
ListenableFuture<CachedApiKeyHashResult> cachedValue = service.getApiKeyAuthCache().get(id); | ||
assertThat("since the request was rejected, there should be no cache entry for this key", cachedValue, nullValue()); | ||
|
||
// unblock the threadpool | ||
safeAwait(barrier); | ||
|
||
// wait for the threadpool queue to drain & check that the stats as as expected | ||
flushThreadPoolExecutor(threadPool, SECURITY_CRYPTO_THREAD_POOL_NAME); | ||
for (var stat : threadPool.stats().stats()) { | ||
if (stat.name().equals(SECURITY_CRYPTO_THREAD_POOL_NAME)) { | ||
assertThat(stat.rejected(), equalTo(1L)); | ||
assertThat(stat.queue(), equalTo(0)); | ||
} | ||
} | ||
|
||
// try to authenticate again with the same key - if this hangs, check the future caching | ||
final AuthenticationResult<User> shouldSucceed = tryAuthenticate(service, id, key, type); | ||
assertThat(shouldSucceed.getStatus(), is(AuthenticationResult.Status.SUCCESS)); | ||
} finally { | ||
terminate(clientThreadpool); | ||
} | ||
} | ||
|
||
public void testCreateApiKeyUsesBulkIndexAction() throws Exception { | ||
final Settings settings = Settings.builder().put(XPackSettings.API_KEY_SERVICE_ENABLED_SETTING.getKey(), true).build(); | ||
final ApiKeyService service = createApiKeyService(settings); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm concerned that this might cause log spam, but I don't want to pull it all the way back to debug because I think it's good to be able to easily tell when this is happening.