diff --git a/sigstore-cli/src/main/java/dev/sigstore/cli/TokenStringOidcClient.java b/sigstore-cli/src/main/java/dev/sigstore/cli/TokenStringOidcClient.java index ac5629f08..4fffec110 100644 --- a/sigstore-cli/src/main/java/dev/sigstore/cli/TokenStringOidcClient.java +++ b/sigstore-cli/src/main/java/dev/sigstore/cli/TokenStringOidcClient.java @@ -22,6 +22,7 @@ import dev.sigstore.oidc.client.OidcException; import dev.sigstore.oidc.client.OidcToken; import java.io.IOException; +import java.util.Map; public class TokenStringOidcClient implements OidcClient { @@ -32,12 +33,12 @@ public TokenStringOidcClient(String idToken) { } @Override - public boolean isEnabled() { + public boolean isEnabled(Map env) { return true; } @Override - public OidcToken getIDToken() throws OidcException { + public OidcToken getIDToken(Map env) throws OidcException { try { var jws = JsonWebSignature.parse(new GsonFactory(), idToken); return ImmutableOidcToken.builder() diff --git a/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioClient.java b/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioClient.java index 40d8cd2ec..8154a23a4 100644 --- a/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/fulcio/client/FulcioClient.java @@ -23,8 +23,10 @@ import dev.sigstore.fulcio.v2.CertificateChain; import dev.sigstore.fulcio.v2.CreateSigningCertificateRequest; import dev.sigstore.fulcio.v2.Credentials; +import dev.sigstore.fulcio.v2.GetTrustBundleRequest; import dev.sigstore.fulcio.v2.PublicKey; import dev.sigstore.fulcio.v2.PublicKeyRequest; +import dev.sigstore.fulcio.v2.TrustBundle; import dev.sigstore.http.GrpcChannels; import dev.sigstore.http.HttpParams; import dev.sigstore.http.ImmutableHttpParams; @@ -80,6 +82,15 @@ public FulcioClient build() { } } + public TrustBundle trustBundle() throws Exception { + var channel = GrpcChannels.newManagedChannel(uri.getAuthority(), httpParams); + try { + var client = CAGrpc.newBlockingStub(channel); + return client.getTrustBundle(GetTrustBundleRequest.newBuilder().build()); + } finally { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + } /** * Request a signing certificate from fulcio. * diff --git a/sigstore-java/src/main/java/dev/sigstore/oidc/client/GithubActionsOidcClient.java b/sigstore-java/src/main/java/dev/sigstore/oidc/client/GithubActionsOidcClient.java index ca62b2e5b..21c2ac572 100644 --- a/sigstore-java/src/main/java/dev/sigstore/oidc/client/GithubActionsOidcClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/oidc/client/GithubActionsOidcClient.java @@ -25,6 +25,7 @@ import dev.sigstore.http.ImmutableHttpParams; import io.grpc.Internal; import java.io.IOException; +import java.util.Map; import java.util.logging.Logger; /** @@ -35,9 +36,9 @@ public class GithubActionsOidcClient implements OidcClient { private static final Logger log = Logger.getLogger(GithubActionsOidcClient.class.getName()); - private static final String GITHUB_ACTIONS_KEY = "GITHUB_ACTIONS"; - private static final String REQUEST_TOKEN_KEY = "ACTIONS_ID_TOKEN_REQUEST_TOKEN"; - private static final String REQUEST_URL_KEY = "ACTIONS_ID_TOKEN_REQUEST_URL"; + static final String GITHUB_ACTIONS_KEY = "GITHUB_ACTIONS"; + static final String REQUEST_TOKEN_KEY = "ACTIONS_ID_TOKEN_REQUEST_TOKEN"; + static final String REQUEST_URL_KEY = "ACTIONS_ID_TOKEN_REQUEST_URL"; private static final String DEFAULT_AUDIENCE = "sigstore"; @@ -75,19 +76,25 @@ public GithubActionsOidcClient build() { } @Override - public boolean isEnabled() { - var githubActions = System.getenv(GITHUB_ACTIONS_KEY); + public boolean isEnabled(Map env) { + var githubActions = env.get(GITHUB_ACTIONS_KEY); if (githubActions == null || githubActions.isEmpty()) { log.fine("Github env not detected: skipping github actions oidc"); return false; } + var bearer = env.get(REQUEST_TOKEN_KEY); + var urlBase = env.get(REQUEST_URL_KEY); + if (bearer == null || bearer.isEmpty() || urlBase == null || urlBase.isEmpty()) { + log.info("Github env detected, but github idtoken not found: skipping github actions oidc"); + return false; + } return true; } @Override - public OidcToken getIDToken() throws OidcException { - var bearer = System.getenv(REQUEST_TOKEN_KEY); - var urlBase = System.getenv(REQUEST_URL_KEY); + public OidcToken getIDToken(Map env) throws OidcException { + var bearer = env.get(REQUEST_TOKEN_KEY); + var urlBase = env.get(REQUEST_URL_KEY); if (bearer == null) { throw new OidcException( "Could not get github actions environment variable '" + REQUEST_TOKEN_KEY + "'"); diff --git a/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClient.java b/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClient.java index 69ccfdac5..4eaf31242 100644 --- a/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClient.java @@ -15,15 +15,18 @@ */ package dev.sigstore.oidc.client; +import java.util.Map; + public interface OidcClient { /** * Determine if this client can be used in the current environment. For example, we can ignore * Oidc Clients that are scoped to a specific CI environment * + * @param env the configured system environment * @return true if we should use credentials from this client */ - boolean isEnabled(); + boolean isEnabled(Map env); - OidcToken getIDToken() throws OidcException; + OidcToken getIDToken(Map env) throws OidcException; } diff --git a/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClients.java b/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClients.java index 68e3affac..12bcbb949 100644 --- a/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClients.java +++ b/sigstore-java/src/main/java/dev/sigstore/oidc/client/OidcClients.java @@ -16,10 +16,14 @@ package dev.sigstore.oidc.client; import com.google.common.collect.ImmutableList; +import java.util.Map; +import java.util.logging.Logger; /** An ordered list of oidc clients to use when looking for credentials. */ public class OidcClients { + private static final Logger log = Logger.getLogger(OidcClients.class.getName()); + public static final OidcClients PUBLIC_GOOD = of(GithubActionsOidcClient.builder().build(), WebOidcClient.builder().build()); @@ -29,13 +33,15 @@ public class OidcClients { WebOidcClient.builder().setIssuer(WebOidcClient.STAGING_DEX_ISSUER).build()); private final ImmutableList clients; + private final Map env; public static OidcClients of(OidcClient... clients) { - return new OidcClients(ImmutableList.copyOf(clients)); + return new OidcClients(ImmutableList.copyOf(clients), System.getenv()); } - private OidcClients(ImmutableList clients) { + private OidcClients(ImmutableList clients, Map env) { this.clients = clients; + this.env = env; } /** @@ -47,10 +53,12 @@ private OidcClients(ImmutableList clients) { */ public OidcToken getIDToken() throws OidcException { for (var client : clients) { - if (client.isEnabled()) { - return client.getIDToken(); + if (client.isEnabled(env)) { + return client.getIDToken(env); } } + log.info( + "Could not find an oidc provider, if you are in CI make sure the token is available to the sigstore signing process"); throw new OidcException("Could not find an oidc provider"); } } diff --git a/sigstore-java/src/main/java/dev/sigstore/oidc/client/WebOidcClient.java b/sigstore-java/src/main/java/dev/sigstore/oidc/client/WebOidcClient.java index f069e9b94..604e4846c 100644 --- a/sigstore-java/src/main/java/dev/sigstore/oidc/client/WebOidcClient.java +++ b/sigstore-java/src/main/java/dev/sigstore/oidc/client/WebOidcClient.java @@ -38,12 +38,16 @@ import java.io.IOException; import java.util.Arrays; import java.util.Locale; +import java.util.Map; +import java.util.logging.Logger; /** * A client to obtain oidc tokens from an oauth provider via web workflow for use with sigstore. By * default this client is configued to use the public sigstore dex instance. */ public class WebOidcClient implements OidcClient { + private static final Logger log = Logger.getLogger(WebOidcClient.class.getName()); + public static final String PUBLIC_DEX_ISSUER = "https://oauth2.sigstore.dev/auth"; public static final String STAGING_DEX_ISSUER = "https://oauth2.sigstage.dev/auth"; @@ -112,12 +116,13 @@ public WebOidcClient build() { } } - /** - * This provider is always enabled by default, however it should be lower priority over other - * providers which obtain ambient credentials. - */ + /** This provider is usually enabled unless we're in CI. */ @Override - public boolean isEnabled() { + public boolean isEnabled(Map env) { + if ("true".equalsIgnoreCase(env.get("CI"))) { + log.info("Skipping browser based oidc provider because CI detected"); + return false; + } return true; } @@ -128,7 +133,7 @@ public boolean isEnabled() { * @throws OidcException if an error occurs doing the authorization flow */ @Override - public OidcToken getIDToken() throws OidcException { + public OidcToken getIDToken(Map env) throws OidcException { JsonFactory jsonFactory = new GsonFactory(); HttpTransport httpTransport = HttpClients.newHttpTransport(httpParams); DataStoreFactory memStoreFactory = new MemoryDataStoreFactory(); diff --git a/sigstore-java/src/test/java/dev/sigstore/KeylessSignerTest.java b/sigstore-java/src/test/java/dev/sigstore/KeylessSignerTest.java index da0f58d85..c625391f3 100644 --- a/sigstore-java/src/test/java/dev/sigstore/KeylessSignerTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/KeylessSignerTest.java @@ -129,7 +129,8 @@ public void sign_passGithubOidcCheck() throws Exception { // silly way to get the right oidc identity to make sure our simple matcher works var jws = JsonWebSignature.parse( - new GsonFactory(), GithubActionsOidcClient.builder().build().getIDToken().getIdToken()); + new GsonFactory(), + GithubActionsOidcClient.builder().build().getIDToken(System.getenv()).getIdToken()); var expectedGithubSubject = jws.getPayload().getSubject(); var signer = KeylessSigner.builder() diff --git a/sigstore-java/src/test/java/dev/sigstore/oidc/client/GithubActionsOidcClientTest.java b/sigstore-java/src/test/java/dev/sigstore/oidc/client/GithubActionsOidcClientTest.java index dfd9370b6..afc21e35a 100644 --- a/sigstore-java/src/test/java/dev/sigstore/oidc/client/GithubActionsOidcClientTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/oidc/client/GithubActionsOidcClientTest.java @@ -17,18 +17,56 @@ import dev.sigstore.testkit.annotations.EnabledIfOidcExists; import dev.sigstore.testkit.annotations.OidcProviderType; +import io.github.netmikey.logunit.api.LogCapturer; +import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; public class GithubActionsOidcClientTest { + @RegisterExtension + LogCapturer logs = + LogCapturer.create().captureForType(GithubActionsOidcClient.class, Level.DEBUG); + @Test @EnabledIfOidcExists(provider = OidcProviderType.GITHUB) public void getToken() throws OidcException { var client = GithubActionsOidcClient.builder().build(); - var token = client.getIDToken(); + var token = client.getIDToken(System.getenv()); Assertions.assertNotNull(token.getSubjectAlternativeName()); Assertions.assertNotNull(token.getIdToken()); } + + @Test + public void isEnabled_github() { + var client = GithubActionsOidcClient.builder().build(); + var env = + Map.of( + GithubActionsOidcClient.GITHUB_ACTIONS_KEY, + "ignored", + GithubActionsOidcClient.REQUEST_TOKEN_KEY, + "ignored", + GithubActionsOidcClient.REQUEST_URL_KEY, + "ignored"); + Assertions.assertTrue(client.isEnabled(env)); + } + + @Test + public void isEnabled_githubButNoTokenInfo() { + var client = GithubActionsOidcClient.builder().build(); + var env = Map.of(GithubActionsOidcClient.GITHUB_ACTIONS_KEY, "ignored"); + Assertions.assertFalse(client.isEnabled(env)); + logs.assertContains( + "Github env detected, but github idtoken not found: skipping github actions oidc"); + } + + @Test + public void isEnabled_notGithub() { + var client = GithubActionsOidcClient.builder().build(); + Assertions.assertFalse(client.isEnabled(Map.of())); + logs.assertContains("Github env not detected: skipping github actions oidc"); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/oidc/client/WebOidcClientTest.java b/sigstore-java/src/test/java/dev/sigstore/oidc/client/WebOidcClientTest.java index 0b7d8ffa0..3d0ae60c1 100644 --- a/sigstore-java/src/test/java/dev/sigstore/oidc/client/WebOidcClientTest.java +++ b/sigstore-java/src/test/java/dev/sigstore/oidc/client/WebOidcClientTest.java @@ -17,15 +17,21 @@ import com.gargoylesoftware.htmlunit.WebClient; import dev.sigstore.testing.MockOAuth2ServerExtension; +import io.github.netmikey.logunit.api.LogCapturer; +import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; public class WebOidcClientTest { @RegisterExtension private static final MockOAuth2ServerExtension server = new MockOAuth2ServerExtension(); + @RegisterExtension + LogCapturer logs = LogCapturer.create().captureForType(WebOidcClient.class, Level.DEBUG); + @Test public void testAuthFlow() throws OidcException { try (var webClient = new WebClient()) { @@ -35,9 +41,22 @@ public void testAuthFlow() throws OidcException { .setBrowser(webClient::getPage) .build(); - var eid = oidcClient.getIDToken(); + var eid = oidcClient.getIDToken(System.getenv()); Assertions.assertEquals( MockOAuth2ServerExtension.DEFAULT_CONFIGURED_EMAIL, eid.getSubjectAlternativeName()); } } + + @Test + public void isEnabled_CI() { + var client = WebOidcClient.builder().build(); + Assertions.assertFalse(client.isEnabled(Map.of("CI", "true"))); + logs.assertContains("Skipping browser based oidc provider because CI detected"); + } + + @Test + public void isEnabled_notCI() { + var client = WebOidcClient.builder().build(); + Assertions.assertTrue(client.isEnabled(Map.of("CI", "false"))); + } } diff --git a/sigstore-java/src/test/java/dev/sigstore/testing/MockOAuth2ServerExtension.java b/sigstore-java/src/test/java/dev/sigstore/testing/MockOAuth2ServerExtension.java index 75b025ded..f913e085f 100644 --- a/sigstore-java/src/test/java/dev/sigstore/testing/MockOAuth2ServerExtension.java +++ b/sigstore-java/src/test/java/dev/sigstore/testing/MockOAuth2ServerExtension.java @@ -70,7 +70,7 @@ public OidcToken getOidcToken() throws OidcException { .setBrowser(webClient::getPage) .build(); - return oidcClient.getIDToken(); + return oidcClient.getIDToken(System.getenv()); } }