Skip to content

Commit

Permalink
Fixed InstanceJenkinsProvider (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
ctyano authored Sep 20, 2024
1 parent 04d6d67 commit e0f7626
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,8 @@ public class InstanceJenkinsProvider implements InstanceProvider {
static final String JENKINS_PROP_ISSUER = "athenz.zts.jenkins.issuer";
static final String JENKINS_PROP_JWKS_URI = "athenz.zts.jenkins.jwks_uri";

static final String JENKINS_ISSUER = "https://token.actions.githubusercontent.com";
static final String JENKINS_ISSUER_JWKS_URI = "https://token.actions.githubusercontent.com/.well-known/jwks";

public static final String CLAIM_RUN_ID = "run_id";
public static final String CLAIM_EVENT_NAME = "event_name";
public static final String CLAIM_REPOSITORY = "repository";
static final String JENKINS_ISSUER = "https://jenkins.athenz.svc.cluster.local/oidc";
static final String JENKINS_ISSUER_JWKS_URI = "https://jenkins.athenz.svc.cluster.local/oidc/jwks";

Set<String> dnsSuffixes = null;
String jenkinsIssuer = null;
Expand Down Expand Up @@ -161,14 +157,14 @@ public InstanceConfirmation confirmInstance(InstanceConfirmation confirmation) {

if (!StringUtil.isEmpty(InstanceUtils.getInstanceProperty(instanceAttributes,
InstanceProvider.ZTS_INSTANCE_HOSTNAME))) {
throw forbiddenError("Request must not have any hostname values");
throw forbiddenError("Request must not have any sanDNS values");
}

// validate san URI

if (!validateSanUri(InstanceUtils.getInstanceProperty(instanceAttributes,
InstanceProvider.ZTS_INSTANCE_SAN_URI))) {
throw forbiddenError("Unable to validate certificate request URI values");
throw forbiddenError("Unable to validate certificate request sanURI values");
}

// we need to validate the token which is our attestation
Expand All @@ -177,14 +173,14 @@ public InstanceConfirmation confirmInstance(InstanceConfirmation confirmation) {

final String attestationData = confirmation.getAttestationData();
if (StringUtil.isEmpty(attestationData)) {
throw forbiddenError("Service credentials not provided");
throw forbiddenError("Jenkins ID Token must be provided");
}

StringBuilder errMsg = new StringBuilder(256);
final String reqInstanceId = InstanceUtils.getInstanceProperty(instanceAttributes,
InstanceProvider.ZTS_INSTANCE_ID);
if (!validateOIDCToken(attestationData, instanceDomain, instanceService, reqInstanceId, errMsg)) {
throw forbiddenError("Unable to validate Certificate Request: " + errMsg.toString());
throw forbiddenError("Unable to validate Certificate Request with the provided ID Token: " + errMsg.toString());
}

// validate the certificate san DNS names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.security.PrivateKey;
import java.time.Instant;
import java.util.Date;
Expand Down Expand Up @@ -61,55 +62,55 @@ public void testInitializeWithConfig() {
assertNotNull(provider.getHttpDriver("https://config.athenz.io"));
}

// @Test
// public void testInitializeWithHttpDriver() throws IOException {
//
// // std test where the http driver will return null for the config object
//
// InstanceJenkinsProviderTestImpl provider = new InstanceJenkinsProviderTestImpl();
// HttpDriver httpDriver = Mockito.mock(HttpDriver.class);
// provider.setHttpDriver(httpDriver);
// provider.initialize("sys.auth.jenkins",
// "class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
// assertNotNull(provider);
// assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);
//
// // test where the http driver will return a valid config object
//
// provider = new InstanceJenkinsProviderTestImpl();
// httpDriver = Mockito.mock(HttpDriver.class);
// Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
// .thenReturn("{\"jwks_uri\":\"https://athenz.io/jwks\"}");
// provider.setHttpDriver(httpDriver);
// provider.initialize("sys.auth.jenkins",
// "class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
// assertNotNull(provider);
// assertEquals(provider.signingKeyResolver.getJwksUri(), "https://athenz.io/jwks");
//
// // test when http driver return invalid data
//
// provider = new InstanceJenkinsProviderTestImpl();
// httpDriver = Mockito.mock(HttpDriver.class);
// Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
// .thenReturn("invalid-json");
// provider.setHttpDriver(httpDriver);
// provider.initialize("sys.auth.jenkins",
// "class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
// assertNotNull(provider);
// assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);
//
// // and finally throwing an exception
//
// provider = new InstanceJenkinsProviderTestImpl();
// httpDriver = Mockito.mock(HttpDriver.class);
// Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
// .thenThrow(new IOException("invalid-json"));
// provider.setHttpDriver(httpDriver);
// provider.initialize("sys.auth.jenkins",
// "class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
// assertNotNull(provider);
// assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);
// }
@Test
public void testInitializeWithHttpDriver() throws IOException {

// std test where the http driver will return null for the config object

InstanceJenkinsProviderTestImpl provider = new InstanceJenkinsProviderTestImpl();
HttpDriver httpDriver = Mockito.mock(HttpDriver.class);
provider.setHttpDriver(httpDriver);
provider.initialize("sys.auth.jenkins",
"class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
assertNotNull(provider);
assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);

// test where the http driver will return a valid config object

provider = new InstanceJenkinsProviderTestImpl();
httpDriver = Mockito.mock(HttpDriver.class);
Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
.thenReturn("{\"jwks_uri\":\"https://athenz.io/jwks\"}");
provider.setHttpDriver(httpDriver);
provider.initialize("sys.auth.jenkins",
"class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
assertNotNull(provider);
assertEquals(provider.signingKeyResolver.getJwksUri(), "https://athenz.io/jwks");

// test when http driver return invalid data

provider = new InstanceJenkinsProviderTestImpl();
httpDriver = Mockito.mock(HttpDriver.class);
Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
.thenReturn("invalid-json");
provider.setHttpDriver(httpDriver);
provider.initialize("sys.auth.jenkins",
"class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
assertNotNull(provider);
assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);

// and finally throwing an exception

provider = new InstanceJenkinsProviderTestImpl();
httpDriver = Mockito.mock(HttpDriver.class);
Mockito.when(httpDriver.doGet("/.well-known/openid-configuration", null))
.thenThrow(new IOException("invalid-json"));
provider.setHttpDriver(httpDriver);
provider.initialize("sys.auth.jenkins",
"class://com.yahoo.athenz.instance.provider.impl.InstanceJenkinsProvider", null, null);
assertNotNull(provider);
assertEquals(provider.signingKeyResolver.getJwksUri(), InstanceJenkinsProvider.JENKINS_ISSUER_JWKS_URI);
}

@Test
public void testConfirmInstance() {
Expand All @@ -136,7 +137,7 @@ public void testConfirmInstance() {
confirmation.setDomain("sports");
confirmation.setService("api");
confirmation.setProvider("sys.auth.jenkins");
confirmation.setAttestationData(generateIdToken("https://token.actions.githubusercontent.com",
confirmation.setAttestationData(generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, false, false, false, false, false));
confirmation.setAttributes(instanceAttributes);

Expand Down Expand Up @@ -173,7 +174,7 @@ public void testConfirmInstanceFailures() {
confirmation.setDomain("sports");
confirmation.setService("api");
confirmation.setProvider("sys.auth.jenkins");
confirmation.setAttestationData(generateIdToken("https://token.actions.githubusercontent.com",
confirmation.setAttestationData(generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, false, false, false, false, false));
confirmation.setAttributes(instanceAttributes);

Expand All @@ -184,7 +185,8 @@ public void testConfirmInstanceFailures() {
fail();
} catch (ResourceException ex) {
assertEquals(ex.getCode(), 403);
assertTrue(ex.getMessage().contains("Unable to validate Certificate Request: Unable to parse and validate token with JWKs: A signing key must be specified if the specified JWT is digitally signed."));
assertTrue(ex.getMessage().contains("Unable to validate Certificate Request with the provided ID Token: "));
assertTrue(ex.getMessage().contains("Unable to parse and validate token with JWKs: A signing key must be specified if the specified JWT is digitally signed."));
}

// once we add the expected public key we should get a failure due to invalid san dns entry
Expand Down Expand Up @@ -259,7 +261,7 @@ public void testConfirmInstanceWithHostname() {
fail();
} catch (ResourceException ex) {
assertEquals(ex.getCode(), 403);
assertTrue(ex.getMessage().contains("Request must not have any hostname values"));
assertTrue(ex.getMessage().contains("Request must not have any sanDNS values"));
}
}

Expand All @@ -283,7 +285,7 @@ public void testConfirmInstanceWithSanURI() {
fail();
} catch (ResourceException ex) {
assertEquals(ex.getCode(), 403);
assertTrue(ex.getMessage().contains("Unable to validate certificate request URI values"));
assertTrue(ex.getMessage().contains("Unable to validate certificate request sanURI values"));
}
}

Expand All @@ -303,7 +305,7 @@ public void testConfirmInstanceWithoutAttestationData() {
fail();
} catch (ResourceException ex) {
assertEquals(ex.getCode(), 403);
assertTrue(ex.getMessage().contains("Service credentials not provided"));
assertTrue(ex.getMessage().contains("Jenkins ID Token must be provided"));
}
}

Expand Down Expand Up @@ -369,7 +371,7 @@ public void testValidateOIDCTokenAudienceMismatch() {

// our audience will not match

String idToken = generateIdToken("https://token.actions.githubusercontent.com",
String idToken = generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, false, false, false, false, false);
StringBuilder errMsg = new StringBuilder(256);
boolean result = provider.validateOIDCToken(idToken, "sports", "api", "athenz:sia:0001", errMsg);
Expand All @@ -390,7 +392,7 @@ public void testValidateOIDCTokenStartNotRecentEnough() {

// our issue time is not recent enough

String idToken = generateIdToken("https://token.actions.githubusercontent.com",
String idToken = generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000 - 400, false, false, false, false, false);
StringBuilder errMsg = new StringBuilder(256);
boolean result = provider.validateOIDCToken(idToken, "sports", "api", "athenz:sia:0001", errMsg);
Expand All @@ -399,7 +401,7 @@ public void testValidateOIDCTokenStartNotRecentEnough() {

// create another token without the issue time

idToken = generateIdToken("https://token.actions.githubusercontent.com",
idToken = generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, false, false, true, false, false);
errMsg.setLength(0);
result = provider.validateOIDCToken(idToken, "sports", "api", "athenz:sia:0001", errMsg);
Expand All @@ -420,7 +422,7 @@ public void testValidateOIDCTokenMissingSubject() {

// create an id token without the subject claim

String idToken = generateIdToken("https://token.actions.githubusercontent.com",
String idToken = generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, true, false, false, false, false);

StringBuilder errMsg = new StringBuilder(256);
Expand Down Expand Up @@ -449,7 +451,7 @@ public void testValidateOIDCTokenAuthorizationFailure() {

// create an id token

String idToken = generateIdToken("https://token.actions.githubusercontent.com",
String idToken = generateIdToken("https://jenkins.athenz.svc.cluster.local/oidc",
System.currentTimeMillis() / 1000, false, false, false, false, false);

StringBuilder errMsg = new StringBuilder(256);
Expand Down

0 comments on commit e0f7626

Please sign in to comment.