Skip to content

Commit

Permalink
[Feature/Extension] Add cluster id check for OBO Authenticator (opens…
Browse files Browse the repository at this point in the history
…earch-project#3117)


---------

Signed-off-by: Ryan Liang <[email protected]>
  • Loading branch information
RyanL1997 authored Aug 9, 2023
1 parent 058f8ec commit 30cf5b1
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin
private volatile ThreadPool threadPool;
private volatile ConfigurationRepository cr;
private volatile AdminDNs adminDns;
private volatile ClusterService cs;
private static volatile ClusterService cs;
private volatile AtomicReference<DiscoveryNode> localNode = new AtomicReference<>();
private volatile AuditLog auditLog;
private volatile BackendRegistry backendRegistry;
Expand Down Expand Up @@ -1959,4 +1959,12 @@ public void start() {}
public void stop() {}

}

public static void setClusterService(ClusterService clusterService) {
cs = clusterService;
}

public static ClusterService getClusterNameString() {
return cs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.rest.RestChannel;
import org.opensearch.rest.RestRequest;
import org.opensearch.security.OpenSearchSecurityPlugin;
import org.opensearch.security.auth.HTTPAuthenticator;
import org.opensearch.security.authtoken.jwt.EncryptionDecryptionUtil;
import org.opensearch.security.ssl.util.ExceptionUtils;
Expand Down Expand Up @@ -209,6 +210,13 @@ private AuthCredentials extractCredentials0(final RestRequest request) {
return null;
}

final String issuer = claims.getIssuer();
final String clusterID = OpenSearchSecurityPlugin.getClusterNameString().getClusterName().value();
if (!issuer.equals(clusterID)) {
log.error("This issuer of this OBO does not match the current cluster identifier");
return null;
}

List<String> roles = extractSecurityRolesFromClaims(claims);
String[] backendRoles = extractBackendRolesFromClaims(claims);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hc.core5.http.HttpHeaders;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import org.opensearch.cluster.ClusterName;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.settings.Settings;
import org.opensearch.security.OpenSearchSecurityPlugin;
import org.opensearch.security.user.AuthCredentials;
import org.opensearch.security.util.FakeRestRequest;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class OnBehalfOfAuthenticatorTest {
final static String clusterNameString = "cluster_0";
final static String enableOBO = "true";
final static String disableOBO = "false";
final static String claimsEncryptionKey = RandomStringUtils.randomAlphanumeric(16);
Expand All @@ -44,14 +53,28 @@ public class OnBehalfOfAuthenticatorTest {
final static String signingKeyB64Encoded = BaseEncoding.base64().encode(signingKey.getBytes(StandardCharsets.UTF_8));
final static SecretKey secretKey = Keys.hmacShaKeyFor(signingKeyB64Encoded.getBytes(StandardCharsets.UTF_8));

@Before
public void setupMocks() {
ClusterService mockedClusterService = mock(ClusterService.class);
ClusterName mockedClusterName = new ClusterName(clusterNameString);
when(mockedClusterService.getClusterName()).thenReturn(mockedClusterName);

OpenSearchSecurityPlugin.setClusterService(mockedClusterService);
}

@After
public void tearDown() {
OpenSearchSecurityPlugin.setClusterService(null);
}

@Test
public void testNoKey() throws Exception {

try {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
null,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy"),
Jwts.builder().setIssuer(clusterNameString).claim("typ", "obo").setSubject("Leonard McCoy"),
false
);
Assert.fail("Expected a RuntimeException");
Expand All @@ -67,7 +90,7 @@ public void testEmptyKey() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
null,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy"),
Jwts.builder().setIssuer(clusterNameString).claim("typ", "obo").setSubject("Leonard McCoy"),
false
);
Assert.fail("Expected a RuntimeException");
Expand All @@ -83,7 +106,7 @@ public void testBadKey() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
BaseEncoding.base64().encode(new byte[] { 1, 3, 3, 4, 3, 6, 7, 8, 3, 10 }),
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy"),
Jwts.builder().setIssuer(clusterNameString).claim("typ", "obo").setSubject("Leonard McCoy"),
false
);
Assert.fail("Expected a WeakKeyException");
Expand Down Expand Up @@ -119,6 +142,7 @@ public void testInvalid() throws Exception {
@Test
public void testDisabled() throws Exception {
String jwsToken = Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
Expand All @@ -136,6 +160,7 @@ public void testDisabled() throws Exception {
@Test
public void testNonSpecifyOBOSetting() throws Exception {
String jwsToken = Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
Expand All @@ -154,6 +179,7 @@ public void testNonSpecifyOBOSetting() throws Exception {
public void testBearer() throws Exception {

String jwsToken = Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
Expand All @@ -170,13 +196,14 @@ public void testBearer() throws Exception {
Assert.assertEquals("Leonard McCoy", credentials.getUsername());
Assert.assertEquals(0, credentials.getSecurityRoles().size());
Assert.assertEquals(0, credentials.getBackendRoles().size());
Assert.assertEquals(3, credentials.getAttributes().size());
Assert.assertEquals(4, credentials.getAttributes().size());
}

@Test
public void testBearerWrongPosition() throws Exception {

String jwsToken = Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
Expand All @@ -195,6 +222,7 @@ public void testBearerWrongPosition() throws Exception {
@Test
public void testBasicAuthHeader() throws Exception {
String jwsToken = Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
Expand All @@ -214,7 +242,12 @@ public void testRoles() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy").claim("dr", "role1,role2").setAudience("svc1"),
Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.claim("dr", "role1,role2")
.setAudience("svc1"),
true
);

Expand All @@ -230,7 +263,7 @@ public void testNoTokenType() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().setSubject("Leonard McCoy").claim("dr", "role1,role2").setAudience("svc1"),
Jwts.builder().setIssuer(clusterNameString).setSubject("Leonard McCoy").claim("dr", "role1,role2").setAudience("svc1"),
true
);

Expand All @@ -243,7 +276,12 @@ public void testNullClaim() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy").claim("dr", null).setAudience("svc1"),
Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.claim("dr", null)
.setAudience("svc1"),
false
);

Expand All @@ -258,7 +296,12 @@ public void testNonStringClaim() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy").claim("dr", 123L).setAudience("svc1"),
Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.claim("dr", 123L)
.setAudience("svc1"),
true
);

Expand All @@ -274,7 +317,7 @@ public void testRolesMissing() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Leonard McCoy").setAudience("svc1"),
Jwts.builder().setIssuer(clusterNameString).claim("typ", "obo").setSubject("Leonard McCoy").setAudience("svc1"),
false
);

Expand All @@ -290,7 +333,12 @@ public void testWrongSubjectKey() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").claim("roles", "role1,role2").claim("asub", "Dr. Who").setAudience("svc1"),
Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.claim("roles", "role1,role2")
.claim("asub", "Dr. Who")
.setAudience("svc1"),
false
);

Expand All @@ -303,7 +351,7 @@ public void testExp() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Expired").setExpiration(new Date(100)),
Jwts.builder().setIssuer(clusterNameString).claim("typ", "obo").setSubject("Expired").setExpiration(new Date(100)),
false
);

Expand All @@ -316,7 +364,11 @@ public void testNbf() throws Exception {
final AuthCredentials credentials = extractCredentialsFromJwtHeader(
signingKeyB64Encoded,
claimsEncryptionKey,
Jwts.builder().claim("typ", "obo").setSubject("Expired").setNotBefore(new Date(System.currentTimeMillis() + (1000 * 36000))),
Jwts.builder()
.setIssuer(clusterNameString)
.claim("typ", "obo")
.setSubject("Expired")
.setNotBefore(new Date(System.currentTimeMillis() + (1000 * 36000))),
false
);

Expand All @@ -327,7 +379,15 @@ public void testNbf() throws Exception {
public void testRolesArray() throws Exception {

JwtBuilder builder = Jwts.builder()
.setPayload("{" + "\"typ\": \"obo\"," + "\"sub\": \"Cluster_0\"," + "\"aud\": \"ext_0\"," + "\"dr\": \"a,b,3rd\"" + "}");
.setPayload(
"{"
+ "\"iss\": \"cluster_0\","
+ "\"typ\": \"obo\","
+ "\"sub\": \"Cluster_0\","
+ "\"aud\": \"ext_0\","
+ "\"dr\": \"a,b,3rd\""
+ "}"
);

final AuthCredentials credentials = extractCredentialsFromJwtHeader(signingKeyB64Encoded, claimsEncryptionKey, builder, true);

Expand All @@ -339,6 +399,26 @@ public void testRolesArray() throws Exception {
Assert.assertTrue(credentials.getSecurityRoles().contains("3rd"));
}

@Test
public void testDifferentIssuer() throws Exception {

String jwsToken = Jwts.builder()
.setIssuer("Wrong Cluster Identifier")
.claim("typ", "obo")
.setSubject("Leonard McCoy")
.setAudience("ext_0")
.signWith(Keys.hmacShaKeyFor(Base64.getDecoder().decode(signingKeyB64Encoded)), SignatureAlgorithm.HS512)
.compact();

OnBehalfOfAuthenticator jwtAuth = new OnBehalfOfAuthenticator(defaultSettings());
Map<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "Bearer " + jwsToken);

AuthCredentials credentials = jwtAuth.extractCredentials(new FakeRestRequest(headers, new HashMap<String, String>()), null);

Assert.assertNull(credentials);
}

/** extracts a default user credential from a request header */
private AuthCredentials extractCredentialsFromJwtHeader(
final String signingKeyB64Encoded,
Expand Down

0 comments on commit 30cf5b1

Please sign in to comment.