-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tests related to brute force attack prevention. (#2245)
Tests related to brute force attack prevention. Signed-off-by: Lukasz Soszynski <[email protected]>
- Loading branch information
1 parent
dc7d21c
commit 25ea092
Showing
15 changed files
with
716 additions
and
39 deletions.
There are no files selected for viewing
158 changes: 158 additions & 0 deletions
158
src/integrationTest/java/org/opensearch/security/IpBruteForceAttacksPreventionTests.java
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,158 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
* | ||
*/ | ||
package org.opensearch.security; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; | ||
import org.junit.ClassRule; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import org.opensearch.test.framework.AuthFailureListeners; | ||
import org.opensearch.test.framework.RateLimiting; | ||
import org.opensearch.test.framework.TestSecurityConfig.User; | ||
import org.opensearch.test.framework.cluster.ClusterManager; | ||
import org.opensearch.test.framework.cluster.LocalCluster; | ||
import org.opensearch.test.framework.cluster.TestRestClient; | ||
import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; | ||
import org.opensearch.test.framework.cluster.TestRestClientConfiguration; | ||
import org.opensearch.test.framework.log.LogsRule; | ||
|
||
import static org.apache.hc.core5.http.HttpStatus.SC_OK; | ||
import static org.apache.hc.core5.http.HttpStatus.SC_UNAUTHORIZED; | ||
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL_WITHOUT_CHALLENGE; | ||
import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; | ||
import static org.opensearch.test.framework.cluster.TestRestClientConfiguration.userWithSourceIp; | ||
|
||
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) | ||
@ThreadLeakScope(ThreadLeakScope.Scope.NONE) | ||
public class IpBruteForceAttacksPreventionTests { | ||
private static final User USER_1 = new User("simple-user-1").roles(ALL_ACCESS); | ||
private static final User USER_2 = new User("simple-user-2").roles(ALL_ACCESS); | ||
|
||
public static final int ALLOWED_TRIES = 3; | ||
public static final int TIME_WINDOW_SECONDS = 3; | ||
|
||
public static final String CLIENT_IP_2 = "127.0.0.2"; | ||
public static final String CLIENT_IP_3 = "127.0.0.3"; | ||
public static final String CLIENT_IP_4 = "127.0.0.4"; | ||
public static final String CLIENT_IP_5 = "127.0.0.5"; | ||
public static final String CLIENT_IP_6 = "127.0.0.6"; | ||
public static final String CLIENT_IP_7 = "127.0.0.7"; | ||
public static final String CLIENT_IP_8 = "127.0.0.8"; | ||
public static final String CLIENT_IP_9 = "127.0.0.9"; | ||
|
||
private static final AuthFailureListeners listener = new AuthFailureListeners() | ||
.addRateLimit(new RateLimiting("internal_authentication_backend_limiting").type("ip") | ||
.allowedTries(ALLOWED_TRIES).timeWindowSeconds(TIME_WINDOW_SECONDS).blockExpirySeconds(2).maxBlockedClients(500) | ||
.maxTrackedClients(500)); | ||
|
||
@ClassRule | ||
public static final LocalCluster cluster = new LocalCluster.Builder() | ||
.clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false).authFailureListeners(listener) | ||
.authc(AUTHC_HTTPBASIC_INTERNAL_WITHOUT_CHALLENGE).users(USER_1, USER_2).build(); | ||
|
||
@Rule | ||
public LogsRule logsRule = new LogsRule("org.opensearch.security.auth.BackendRegistry"); | ||
|
||
@Test | ||
public void shouldAuthenticateUserWhenBlockadeIsNotActive() { | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_1, CLIENT_IP_2))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldBlockIpAddress() { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_3, USER_2, ALLOWED_TRIES); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_2, CLIENT_IP_3))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
logsRule.assertThatContain("Rejecting REST request because of blocked address: /" + CLIENT_IP_3); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldBlockUsersWhoUseTheSameIpAddress() { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_4, USER_1, ALLOWED_TRIES); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_2, CLIENT_IP_4))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
logsRule.assertThatContain("Rejecting REST request because of blocked address: /" + CLIENT_IP_4); | ||
} | ||
} | ||
|
||
@Test | ||
public void testUserShouldBeAbleToAuthenticateFromAnotherNotBlockedIpAddress() { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_5, USER_1, ALLOWED_TRIES); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_1, CLIENT_IP_6))) { | ||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldNotBlockIpWhenFailureAuthenticationCountIsLessThanAllowedTries() { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_7, USER_1, ALLOWED_TRIES - 1); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_1, CLIENT_IP_7))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldBlockIpWhenFailureAuthenticationCountIsGraterThanAllowedTries() { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_8, USER_1, ALLOWED_TRIES * 2); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_1, CLIENT_IP_8))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
logsRule.assertThatContain("Rejecting REST request because of blocked address: /" + CLIENT_IP_8); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldReleaseIpAddressLock() throws InterruptedException { | ||
authenticateUserWithIncorrectPassword(CLIENT_IP_9, USER_1, ALLOWED_TRIES * 2); | ||
TimeUnit.SECONDS.sleep(TIME_WINDOW_SECONDS); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(userWithSourceIp(USER_1, CLIENT_IP_9))) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
logsRule.assertThatContain("Rejecting REST request because of blocked address: /" + CLIENT_IP_9); | ||
} | ||
} | ||
|
||
private static void authenticateUserWithIncorrectPassword(String sourceIpAddress, User user, int numberOfRequests) { | ||
var clientConfiguration = new TestRestClientConfiguration().username(user.getName()) | ||
.password("incorrect password").sourceInetAddress(sourceIpAddress); | ||
try(TestRestClient client = cluster.createGenericClientRestClient(clientConfiguration)) { | ||
for(int i = 0; i < numberOfRequests; ++i) { | ||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
} | ||
} | ||
} | ||
} |
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
125 changes: 125 additions & 0 deletions
125
src/integrationTest/java/org/opensearch/security/UserBruteForceAttacksPreventionTests.java
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,125 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* The OpenSearch Contributors require contributions made to | ||
* this file be licensed under the Apache-2.0 license or a | ||
* compatible open source license. | ||
* | ||
*/ | ||
package org.opensearch.security; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; | ||
import org.junit.ClassRule; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import org.opensearch.test.framework.AuthFailureListeners; | ||
import org.opensearch.test.framework.RateLimiting; | ||
import org.opensearch.test.framework.TestSecurityConfig.User; | ||
import org.opensearch.test.framework.cluster.ClusterManager; | ||
import org.opensearch.test.framework.cluster.LocalCluster; | ||
import org.opensearch.test.framework.cluster.TestRestClient; | ||
import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; | ||
import org.opensearch.test.framework.log.LogsRule; | ||
|
||
import static org.apache.hc.core5.http.HttpStatus.SC_OK; | ||
import static org.apache.hc.core5.http.HttpStatus.SC_UNAUTHORIZED; | ||
import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; | ||
import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; | ||
|
||
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) | ||
@ThreadLeakScope(ThreadLeakScope.Scope.NONE) | ||
public class UserBruteForceAttacksPreventionTests { | ||
|
||
private static final User USER_1 = new User("simple-user-1").roles(ALL_ACCESS); | ||
private static final User USER_2 = new User("simple-user-2").roles(ALL_ACCESS); | ||
private static final User USER_3 = new User("simple-user-3").roles(ALL_ACCESS); | ||
private static final User USER_4 = new User("simple-user-4").roles(ALL_ACCESS); | ||
private static final User USER_5 = new User("simple-user-5").roles(ALL_ACCESS); | ||
|
||
public static final int ALLOWED_TRIES = 3; | ||
public static final int TIME_WINDOW_SECONDS = 3; | ||
private static final AuthFailureListeners listener = new AuthFailureListeners() | ||
.addRateLimit(new RateLimiting("internal_authentication_backend_limiting").type("username").authenticationBackend("intern") | ||
.allowedTries(ALLOWED_TRIES).timeWindowSeconds(TIME_WINDOW_SECONDS).blockExpirySeconds(2).maxBlockedClients(500) | ||
.maxTrackedClients(500)); | ||
|
||
@ClassRule | ||
public static final LocalCluster cluster = new LocalCluster.Builder() | ||
.clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false).authFailureListeners(listener) | ||
.authc(AUTHC_HTTPBASIC_INTERNAL).users(USER_1, USER_2, USER_3, USER_4, USER_5).build(); | ||
|
||
@Rule | ||
public LogsRule logsRule = new LogsRule("org.opensearch.security.auth.BackendRegistry"); | ||
|
||
@Test | ||
public void shouldAuthenticateUserWhenBlockadeIsNotActive() { | ||
try(TestRestClient client = cluster.getRestClient(USER_1)) { | ||
|
||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldBlockUserWhenNumberOfFailureLoginAttemptIsEqualToLimit() { | ||
authenticateUserWithIncorrectPassword(USER_2, ALLOWED_TRIES); | ||
try(TestRestClient client = cluster.getRestClient(USER_2)) { | ||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
} | ||
//Rejecting REST request because of blocked user: | ||
logsRule.assertThatContain("Rejecting REST request because of blocked user: " + USER_2.getName()); | ||
} | ||
|
||
@Test | ||
public void shouldBlockUserWhenNumberOfFailureLoginAttemptIsGraterThanLimit() { | ||
authenticateUserWithIncorrectPassword(USER_3, ALLOWED_TRIES * 2); | ||
try(TestRestClient client = cluster.getRestClient(USER_3)) { | ||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
} | ||
logsRule.assertThatContain("Rejecting REST request because of blocked user: " + USER_3.getName()); | ||
} | ||
|
||
@Test | ||
public void shouldNotBlockUserWhenNumberOfLoginAttemptIsBelowLimit() { | ||
authenticateUserWithIncorrectPassword(USER_4, ALLOWED_TRIES - 1); | ||
try(TestRestClient client = cluster.getRestClient(USER_4)) { | ||
HttpResponse response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
} | ||
|
||
@Test | ||
public void shouldReleaseLock() throws InterruptedException { | ||
authenticateUserWithIncorrectPassword(USER_5, ALLOWED_TRIES); | ||
try(TestRestClient client = cluster.getRestClient(USER_5)) { | ||
HttpResponse response = client.getAuthInfo(); | ||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
TimeUnit.SECONDS.sleep(TIME_WINDOW_SECONDS); | ||
|
||
response = client.getAuthInfo(); | ||
|
||
response.assertStatusCode(SC_OK); | ||
} | ||
logsRule.assertThatContain("Rejecting REST request because of blocked user: " + USER_5.getName()); | ||
} | ||
|
||
private static void authenticateUserWithIncorrectPassword(User user, int numberOfAttempts) { | ||
try(TestRestClient client = cluster.getRestClient(user.getName(), "incorrect password")) { | ||
for(int i = 0; i < numberOfAttempts; ++i) { | ||
HttpResponse response = client.getAuthInfo(); | ||
response.assertStatusCode(SC_UNAUTHORIZED); | ||
} | ||
} | ||
} | ||
} |
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
Oops, something went wrong.