Skip to content

Commit

Permalink
AWS Configuration Validation and Logging (#724)
Browse files Browse the repository at this point in the history
* broken AWS public key location detected

* ending slash is not considered as error

* public key location logged

* dedicated logger and message for AwsAlbKeyResolver introduced

* algorithm, header and key configuration validation refactoring
  • Loading branch information
AdamBien authored Oct 3, 2023
1 parent f3319ff commit d152878
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.smallrye.jwt.auth.principal;

import java.net.URI;

import org.jose4j.lang.UnresolvableKeyException;

import io.smallrye.jwt.algorithm.SignatureAlgorithm;

interface AwsAlbKeyConfigurationValidator {

public static void validateKeyConfiguration(JWTAuthContextInfo authContextInfo) throws UnresolvableKeyException {
// public key location check
var publicKeyLocation = authContextInfo.getPublicKeyLocation();
if (publicKeyLocation == null) {
throw PrincipalMessages.msg.nullKeyLocation();
}
if (containsSubPath(publicKeyLocation)) {
throw AwsAlbKeyResolverMessages.msg.subPathNotAllowed();
}
}

public static void validatePublicKeyAlgorithmConfiguration(JWTAuthContextInfo authContextInfo) {
var publicKeyAlgorithm = authContextInfo.getSignatureAlgorithm();
if (publicKeyAlgorithm == null) {
AwsAlbKeyResolverLogging.log.publicKeyAlgorithmNotSet();
}
if (!publicKeyAlgorithm.getAlgorithm().equals(SignatureAlgorithm.ES256.getAlgorithm())) {
AwsAlbKeyResolverLogging.log.publicKeyAlgorithmNotSetToES256();
}
}

/**
* verifies the entry: <code>mp.jwt.token.header=X-Amzn-Oidc-Data</code>
*
* @param authContextInfo
*/
public static void validateTokenHeaderConfiguration(JWTAuthContextInfo authContextInfo) {
var tokenHeader = authContextInfo.getTokenHeader();
if (tokenHeader == null || !"X-Amzn-Oidc-Data".equals(tokenHeader)) {
AwsAlbKeyResolverLogging.log.invalidAWSTokenHeader();
}

}

/**
* Remove ending slash from uri e.g. https://localhost:8080/ ->
* https://localhost:8080
*
* @param uri public key location
* @return uri without ending slash
*/
static String removeEndingSlash(String uri) {
if (!uri.endsWith("/") || uri.length() == 1) {
return uri;
}
var length = uri.length();
return uri.substring(0, length - 1);
}

/**
* Check if public key location contains sub path e.g.
* https://localhost:8080/subpath
* Fails fast to prevent runtime errors
*
* @param publicKeyLocation to check
* @return true if public key location contains sub path which is invalid
*/
static boolean containsSubPath(String publicKeyLocation) {
var locationWithoutSlash = removeEndingSlash(publicKeyLocation);
var uri = URI.create(locationWithoutSlash);
return uri.getPath().contains("/");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public class AwsAlbKeyResolver implements VerificationKeyResolver {
private AtomicInteger size = new AtomicInteger();

public AwsAlbKeyResolver(JWTAuthContextInfo authContextInfo) throws UnresolvableKeyException {
if (authContextInfo.getPublicKeyLocation() == null) {
throw PrincipalMessages.msg.nullKeyLocation();
}
AwsAlbKeyConfigurationValidator.validateKeyConfiguration(authContextInfo);
AwsAlbKeyConfigurationValidator.validatePublicKeyAlgorithmConfiguration(authContextInfo);
AwsAlbKeyConfigurationValidator.validateTokenHeaderConfiguration(authContextInfo);
this.authContextInfo = authContextInfo;
this.cacheTimeToLive = Duration.ofMinutes(authContextInfo.getKeyCacheTimeToLive()).toMillis();
}
Expand All @@ -57,6 +57,7 @@ public Key resolveKey(JsonWebSignature jws, List<JsonWebStructure> nestingContex

protected Key retrieveKey(String kid) throws UnresolvableKeyException {
String keyLocation = authContextInfo.getPublicKeyLocation() + "/" + kid;
AwsAlbKeyResolverLogging.log.publicKeyPath(keyLocation);
SimpleResponse simpleResponse = null;
try {
simpleResponse = getHttpGet().get(keyLocation);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.smallrye.jwt.auth.principal;

import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;

@MessageLogger(projectCode = "SRJWT", length = 5)
interface AwsAlbKeyResolverLogging extends BasicLogger {
AwsAlbKeyResolverLogging log = Logger.getMessageLogger(AwsAlbKeyResolverLogging.class,
AwsAlbKeyResolverLogging.class.getPackage().getName());

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 14000, value = "public key path: %s")
void publicKeyPath(String path);

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 14001, value = "mp.jwt.verify.publickey.algorithm is not set."
+ "Falling back to default algorithm: RS256 which is not compabible with AWS ALB."
+ "Please set mp.jwt.verify.publickey.algorithm to ES256")
void publicKeyAlgorithmNotSet();

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 14002, value = "mp.jwt.verify.publickey.algorithm is not set to ES256")
void publicKeyAlgorithmNotSetToES256();

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 14003, value = "mp.jwt.token.header is not set to X-Amzn-Oidc-Data")
void invalidAWSTokenHeader();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.smallrye.jwt.auth.principal;

import org.jboss.logging.Messages;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageBundle;
import org.jose4j.lang.UnresolvableKeyException;

@MessageBundle(projectCode = "SRJWT", length = 5)
interface AwsAlbKeyResolverMessages {
AwsAlbKeyResolverMessages msg = Messages.getBundle(AwsAlbKeyResolverMessages.class);

@Message(id = 15000, value = "Key is resolved from kid. Key location is not allowed. Provide only the path like: https://public-keys.auth.elb.[REGION].amazonaws.com")
UnresolvableKeyException subPathNotAllowed();

}
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,5 @@ interface PrincipalLogging extends BasicLogger {
@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 8044, value = "Encrypted token headers must contain a content type header")
void encryptedTokenMissingContentType();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.smallrye.jwt.auth.principal;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

public class AwsAlbKeyConfigurationValidatorTest {

@Test
void containsSubPath() {
assertTrue(AwsAlbKeyConfigurationValidator
.containsSubPath("https://public-keys.auth.elb.eu-central-1.amazonaws.com/keyid"));
assertTrue(AwsAlbKeyConfigurationValidator
.containsSubPath("https://public-keys.auth.elb.eu-central-1.amazonaws.com/index/keyid"));
assertTrue(AwsAlbKeyConfigurationValidator
.containsSubPath("https://public-keys.auth.elb.eu-central-1.amazonaws.com/index.html"));
assertFalse(
AwsAlbKeyConfigurationValidator.containsSubPath("https://public-keys.auth.elb.eu-central-1.amazonaws.com/"));
assertFalse(AwsAlbKeyConfigurationValidator.containsSubPath("https://public-keys.auth.elb.eu-central-1.amazonaws.com"));
}

@Test
void removeEndingSlash() {
assertTrue(AwsAlbKeyConfigurationValidator.removeEndingSlash("key-location/keyid")
.equals("key-location/keyid"));
assertTrue(AwsAlbKeyConfigurationValidator.removeEndingSlash("key-location/index/keyid/")
.equals("key-location/index/keyid"));
assertTrue(AwsAlbKeyConfigurationValidator.removeEndingSlash("key-location/index.html/")
.equals("key-location/index.html"));
assertTrue(AwsAlbKeyConfigurationValidator.removeEndingSlash("key-location/")
.equals("key-location"));
assertTrue(AwsAlbKeyConfigurationValidator.removeEndingSlash("key-location")
.equals("key-location"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ void loadAwsAlbVerificationKey() throws Exception {
Key key2 = keyLocationResolver.resolveKey(signature, List.of());
assertTrue(key2 == key);
}

}

0 comments on commit d152878

Please sign in to comment.