Skip to content

Commit

Permalink
fix: bundle dependencies and use valid mechanism name
Browse files Browse the repository at this point in the history
  • Loading branch information
kjdelisle committed Jun 10, 2019
1 parent 0d2f4f3 commit d6b74db
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-12">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ buildNumber.properties

# VSCode debug stuff
.vscode

# Mac garbage
.DS_Store
24 changes: 21 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

<groupId>com.stack.security.auth.AWS</groupId>
<artifactId>kafka-auth-aws-iam</artifactId>
<version>1.0-SNAPSHOT</version>
<version>0.2.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<name>Kafka AWS IAM LoginModule</name>
<url>https://github.com/STACK-Fintech</url>
Expand Down Expand Up @@ -59,6 +59,24 @@

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import javax.security.auth.callback.Callback;

/*
* Authentication callback for SASL/AWS-IAM authentication. Callback handler must
* Authentication callback for SASL/AWS authentication. Callback handler must
* set authenticated flag to true if the client provided password in the callback
* matches the expected password.
*/
Expand All @@ -17,11 +17,11 @@ public class AwsIamAuthenticateCallback implements Callback {
* Creates a callback with the password provided by the client
*
* @param accessKeyId The AWS Access Key ID provided by the client during
* SASL/PLAIN authentication
* SASL/AWS authentication
* @param secretAccessKey The AWS Secret Access Key provided by the client
* during SASL/PLAIN authentication
* during SASL/AWS authentication
* @param sessionToken The AWS Session Token provided by the client during
* SASL/PLAIN authentication
* SASL/AWS authentication
* @return
*/
public AwsIamAuthenticateCallback(char[] accessKeyId, char[] secretAccessKey, char[] sessionToken) {
Expand All @@ -31,21 +31,21 @@ public AwsIamAuthenticateCallback(char[] accessKeyId, char[] secretAccessKey, ch
}

/**
* Returns the AWS Access Key ID provided by the client during SASL/AWS-IAM
* Returns the AWS Access Key ID provided by the client during SASL/AWS
*/
public char[] accessKeyId() {
return accessKeyId;
}

/**
* Returns the AWS Secret Access Key provided by the client during SASL/AWS-IAM
* Returns the AWS Secret Access Key provided by the client during SASL/AWS
*/
public char[] secretAccessKey() {
return secretAccessKey;
}

/**
* Returns the AWS Session Token provided by the client during SASL/AWS-IAM
* Returns the AWS Session Token provided by the client during SASL/AWS
*/
public char[] sessionToken() {
return sessionToken;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.stack.security.auth.aws;

import java.util.Map;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
Expand All @@ -23,8 +24,8 @@ public class AwsIamLoginModule implements LoginModule {
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {

var publicCredentials = subject.getPublicCredentials();
var privateCredentials = subject.getPrivateCredentials();
Set<Object> publicCredentials = subject.getPublicCredentials();
Set<Object> privateCredentials = subject.getPrivateCredentials();
String arn = (String) options.get(ARN);
if (arn != null) {
publicCredentials.add(arn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest;
import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult;
import com.stack.security.auth.aws.AwsIamAuthenticateCallback;
import com.stack.security.auth.aws.AwsIamLoginModule;

Expand Down Expand Up @@ -82,7 +84,7 @@ protected boolean authenticate(String arn, char[] accessKeyId, char[] secretAcce
} else {
awsCreds = new BasicAWSCredentials(accessKeyIdString, secretAccessKeyString);
}
var stsService = AWSSecurityTokenServiceClientBuilder.standard()
AWSSecurityTokenService stsService = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build();

// As an added measure of safety, the server can specify what AWS Account ID it
Expand All @@ -92,8 +94,8 @@ protected boolean authenticate(String arn, char[] accessKeyId, char[] secretAcce

// Check the credentials with AWS STS and GetCallerIdentity.

var request = new GetCallerIdentityRequest();
var result = stsService.getCallerIdentity(request);
GetCallerIdentityRequest request = new GetCallerIdentityRequest();
GetCallerIdentityResult result = stsService.getCallerIdentity(request);

// Both the ARN returned by the credentials, and the configured account ID need
// to match!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
Expand All @@ -15,24 +16,29 @@
import javax.security.sasl.SaslServerFactory;

import org.apache.kafka.common.errors.SaslAuthenticationException;
import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler;

import com.stack.security.auth.aws.AwsIamAuthenticateCallback;

/**
* Simple SaslServer implementation for SASL/AWS-IAM. Checks the provided AWS
* Simple SaslServer implementation for SASL/AWS. Checks the provided AWS
* credentials against the AWS STS service and compares the returned identity
* against the one provided by the user, as well as the allowed AWS Account to
* authenticate.
*/
public class AwsIamSaslServer implements SaslServer {

public static final String AWS_IAM_MECHANISM = "AWS-IAM";
public static final String AWS_MECHANISM = "AWS";

private final CallbackHandler callbackHandler;
private final AuthenticateCallbackHandler callbackHandler;
private boolean complete;
private String authorizationId;

public AwsIamSaslServer(CallbackHandler callbackHandler) {
this.callbackHandler = callbackHandler;
if (!(Objects.requireNonNull(callbackHandler) instanceof AuthenticateCallbackHandler))
throw new IllegalArgumentException(String.format("Callback handler must be castable to %s: %s",
AuthenticateCallbackHandler.class.getName(), callbackHandler.getClass().getName()));
this.callbackHandler = (AuthenticateCallbackHandler) callbackHandler;
}

/**
Expand Down Expand Up @@ -96,6 +102,7 @@ public byte[] evaluateResponse(byte[] responseBytes) throws SaslAuthenticationEx
try {
callbackHandler.handle(new Callback[] { nameCallback, authenticateCallback });
} catch (Throwable e) {
e.printStackTrace(System.out);
throw new SaslAuthenticationException("Authentication failed: credentials for user could not be verified", e);
}
if (!authenticateCallback.authenticated())
Expand Down Expand Up @@ -124,8 +131,7 @@ private List<String> extractTokens(String string) {
}

if (tokens.size() < 4 || tokens.size() > 5)
throw new SaslAuthenticationException(
"Invalid SASL/AWS-IAM response: expected 4 or 5 tokens, got " + tokens.size());
throw new SaslAuthenticationException("Invalid SASL/AWS response: expected 4 or 5 tokens, got " + tokens.size());

return tokens;
}
Expand All @@ -139,7 +145,7 @@ public String getAuthorizationID() {

@Override
public String getMechanismName() {
return AWS_IAM_MECHANISM;
return AWS_MECHANISM;
}

@Override
Expand Down Expand Up @@ -178,22 +184,21 @@ public static class AwsIamSaslServerFactory implements SaslServerFactory {
public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map<String, ?> props,
CallbackHandler cbh) throws SaslException {

if (!AWS_IAM_MECHANISM.equals(mechanism))
throw new SaslException(
String.format("Mechanism \'%s\' is not supported. Only AWS-IAM is supported.", mechanism));
if (!AWS_MECHANISM.equals(mechanism))
throw new SaslException(String.format("Mechanism \'%s\' is not supported. Only AWS is supported.", mechanism));

return new AwsIamSaslServer(cbh);
}

@Override
public String[] getMechanismNames(Map<String, ?> props) {
if (props == null)
return new String[] { AWS_IAM_MECHANISM };
return new String[] { AWS_MECHANISM };
String noPlainText = (String) props.get(Sasl.POLICY_NOPLAINTEXT);
if ("true".equals(noPlainText))
return new String[] {};
else
return new String[] { AWS_IAM_MECHANISM };
return new String[] { AWS_MECHANISM };
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public class AwsIamSaslServerProvider extends Provider {

@SuppressWarnings("deprecation")
protected AwsIamSaslServerProvider() {
super("SASL/AWS-IAM Server Provider", 1.0, "SASL/AWS-IAM Server Provider for Kafka");
put("SaslServerFactory." + AwsIamSaslServer.AWS_IAM_MECHANISM, AwsIamSaslServerFactory.class.getName());
super("SASL/AWS Server Provider", 1.0, "SASL/AWS Server Provider for Kafka");
put("SaslServerFactory." + AwsIamSaslServer.AWS_MECHANISM, AwsIamSaslServerFactory.class.getName());
}

public static void initialize() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.Credentials;
import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest;
import com.amazonaws.services.securitytoken.model.GetSessionTokenResult;
import com.stack.security.auth.aws.internal.AwsIamCallbackHandler;
import com.stack.security.auth.aws.internal.AwsIamSaslServer;
import com.stack.security.authenticator.FakeJaasConfig;
Expand All @@ -48,12 +51,12 @@ public class AwsIamSaslServerTest {
@BeforeAll
public static void setUp() {
FakeJaasConfig jaasConfig = new FakeJaasConfig();
var options = new HashMap<String, Object>();
HashMap<String, Object> options = new HashMap<String, Object>();
options.put("aws_account_id", AWS_ACCOUNT_ID);
jaasConfig.addEntry("jaasContext", AwsIamLoginModule.class.getName(), options);
JaasContext jaasContext = new JaasContext("jaasContext", JaasContext.Type.SERVER, jaasConfig, null);
AwsIamCallbackHandler callbackHandler = new AwsIamCallbackHandler();
callbackHandler.configure(null, "AWS-IAM", jaasContext.configurationEntries());
callbackHandler.configure(null, "AWS", jaasContext.configurationEntries());
saslServer = new AwsIamSaslServer(callbackHandler);
}

Expand Down Expand Up @@ -98,11 +101,11 @@ public void emptyTokens() {
e = assertThrows(SaslAuthenticationException.class,
() -> saslServer.evaluateResponse(String.format("%s%s%s%s%s%s%s%s%s%s%s", ARN, nul, ARN, nul, AWS_ACCESS_KEY_ID,
nul, AWS_SECRET_ACCESS_KEY, nul, "s", nul, "q").getBytes(StandardCharsets.UTF_8)));
assertEquals("Invalid SASL/AWS-IAM response: expected 4 or 5 tokens, got 6", e.getMessage());
assertEquals("Invalid SASL/AWS response: expected 4 or 5 tokens, got 6", e.getMessage());

e = assertThrows(SaslAuthenticationException.class, () -> saslServer.evaluateResponse(
String.format("%s%s%s%s", ARN, nul, ARN, nul, AWS_ACCESS_KEY_ID, nul).getBytes(StandardCharsets.UTF_8)));
assertEquals("Invalid SASL/AWS-IAM response: expected 4 or 5 tokens, got 3", e.getMessage());
assertEquals("Invalid SASL/AWS response: expected 4 or 5 tokens, got 3", e.getMessage());
}

@Test
Expand All @@ -118,12 +121,12 @@ public void authorizationFailsForWrongAuthorizationId() {

@Test
public void authorizationSuccessWithValidKeysAndSession() throws Exception {
var awsCreds = new BasicAWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);
var stsService = AWSSecurityTokenServiceClientBuilder.standard()
BasicAWSCredentials awsCreds = new BasicAWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY);
AWSSecurityTokenService stsService = AWSSecurityTokenServiceClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build();
GetSessionTokenRequest request = new GetSessionTokenRequest();
var sessionTokenResult = stsService.getSessionToken(request);
var creds = sessionTokenResult.getCredentials();
GetSessionTokenResult sessionTokenResult = stsService.getSessionToken(request);
Credentials creds = sessionTokenResult.getCredentials();
byte[] nextChallenge = saslServer.evaluateResponse(
saslMessage(ARN, ARN, creds.getAccessKeyId(), creds.getSecretAccessKey(), creds.getSessionToken()));
assertEquals(0, nextChallenge.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void addEntry(String name, String loginModule, Map<String, Object> option
private static String loginModule(String mechanism) {
String loginModule;
switch (mechanism) {
case "AWS-IAM":
case "AWS":
loginModule = AwsIamLoginModule.class.getName();
break;
default:
Expand All @@ -118,7 +118,7 @@ public static Map<String, Object> defaultClientOptions() {
public static Map<String, Object> defaultServerOptions(String mechanism) {
Map<String, Object> options = new HashMap<>();
switch (mechanism) {
case "AWS-IAM":
case "AWS":
options.put(AWS_ACCOUNT_ID, AWS_ACCOUNT_ID_VALUE);
break;
default:
Expand Down

0 comments on commit d6b74db

Please sign in to comment.