Skip to content

Commit 4a53748

Browse files
committed
Push signing config through all our clients
sigstore-gradle now delegates to oidc config is sigstore-java for the default case. Signed-off-by: Appu Goundan <[email protected]>
1 parent af40879 commit 4a53748

File tree

24 files changed

+268
-124
lines changed

24 files changed

+268
-124
lines changed

sigstore-cli/src/main/java/dev/sigstore/cli/Sign.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ public Integer call() throws Exception {
111111
if (identityToken != null) {
112112
// If we've explicitly provided an identity token, customize the signer to only use the token
113113
// string OIDC client.
114-
signerBuilder.oidcClients(OidcClients.of(TokenStringOidcClient.from(identityToken)));
114+
signerBuilder.forceCredentialProviders(
115+
OidcClients.of(TokenStringOidcClient.from(identityToken)));
115116
}
116117
var signer = signerBuilder.build();
117118
var bundle = signer.signFile(artifact);

sigstore-gradle/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,17 @@ dependencies {
5353

5454
sigstoreSign {
5555
oidcClient {
56+
// oidcClient configuration should very rarely be configured, it should be
57+
// inferred from a sigstore deployment's config obtained from a TUF repository
58+
// with a default set of ambient credential providers
5659
gitHub {
5760
audience.set("sigstore")
5861
}
5962
web {
6063
clientId.set("sigstore")
6164
issuer.set("https://oauth2.sigstore.dev/auth")
6265
}
63-
// By default, gitHub client is used if ACTIONS_ID_TOKEN_REQUEST_URL environment variable exists
64-
// This setting would enforce web OIDC client
66+
// override the client config to a specific provider
6567
client.set(web)
6668
// or
6769
client(web)

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/main/kotlin/dev/sigstore/sign/OidcClientExtension.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,8 @@ abstract class OidcClientExtension @Inject constructor(
5050
if (actionsOidcAvailable) {
5151
clients.register<GitHubActionsOidc>(GITHUB_ACTIONS_CLIENT_NAME)
5252
}
53-
54-
client.convention(
55-
clients.named(
56-
if (actionsOidcAvailable) GITHUB_ACTIONS_CLIENT_NAME else WEB_CLIENT_NAME
57-
)
58-
)
53+
// do not assign a default convention for client, that is delegated to KeylessSigner in
54+
// the default case.
5955
}
6056

6157
private inline fun <reified T : OidcClientConfiguration> setup(

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/main/kotlin/dev/sigstore/sign/WebOidc.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717
package dev.sigstore.sign
1818

1919
import dev.sigstore.oidc.client.WebOidcClient
20+
import dev.sigstore.trustroot.ImmutableService
21+
import dev.sigstore.trustroot.ImmutableValidFor
2022
import org.gradle.api.provider.Property
2123
import org.gradle.util.GradleVersion
2224
import java.io.Serializable
25+
import java.net.URI
26+
import java.time.Instant
2327
import javax.inject.Inject
2428

2529
abstract class WebOidc @Inject constructor() : OidcClientConfiguration, Serializable {
@@ -30,7 +34,7 @@ abstract class WebOidc @Inject constructor() : OidcClientConfiguration, Serializ
3034
init {
3135
try {
3236
clientId.convention("sigstore")
33-
issuer.convention(WebOidcClient.PUBLIC_DEX_ISSUER)
37+
issuer.convention("https://oauth2.sigstore.dev/auth")
3438
} catch (e: NullPointerException) {
3539
// NPE here means Gradle tries to isolate WebOidc for passing it to WorkerAction parameters
3640
// Gradle 7.5.1 does not have such an issue, so rethrow unexpected NPEs
@@ -43,7 +47,13 @@ abstract class WebOidc @Inject constructor() : OidcClientConfiguration, Serializ
4347
override fun build(): Any =
4448
WebOidcClient.builder()
4549
.setClientId(clientId.get())
46-
.setIssuer(issuer.get())
50+
.setIssuer(
51+
ImmutableService.builder().apiVersion(1).url(URI.create(issuer.get())).validFor(
52+
ImmutableValidFor.builder().start(
53+
Instant.now()
54+
).build()
55+
).build()
56+
)
4757
.build()
4858

4959
override fun key(): Any = Pair(clientId.get(), issuer.get())

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/main/kotlin/dev/sigstore/sign/work/SignWorkAction.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
package dev.sigstore.sign.work
1818

1919
import dev.sigstore.KeylessSigner
20-
import dev.sigstore.bundle.Bundle
2120
import dev.sigstore.oidc.client.OidcClient
2221
import dev.sigstore.oidc.client.OidcClients
2322
import dev.sigstore.sign.OidcClientConfiguration
2423
import org.gradle.api.file.RegularFileProperty
2524
import org.gradle.api.provider.Property
25+
import org.gradle.internal.impldep.org.hamcrest.core.AnyOf
2626
import org.gradle.workers.WorkAction
2727
import org.gradle.workers.WorkParameters
2828
import org.slf4j.LoggerFactory
@@ -39,6 +39,9 @@ abstract class SignWorkAction : WorkAction<SignWorkParameters> {
3939
private val logger = LoggerFactory.getLogger(SignWorkAction::class.java)
4040

4141
private val clients = ConcurrentHashMap<Any, KeylessSigner>()
42+
43+
// the default key that delegates to KeylessSigners set of default OIDC providers
44+
const val DEFAULT_KEY = "_default"
4245
}
4346

4447
abstract val parameters: SignWorkParameters
@@ -47,11 +50,13 @@ abstract class SignWorkAction : WorkAction<SignWorkParameters> {
4750
val inputFile = parameters.inputFile.get().asFile
4851
logger.info("Signing in Sigstore: {}", inputFile)
4952

50-
val oidcClient = parameters.oidcClient.get()
51-
val signer = clients.computeIfAbsent(oidcClient.key()) {
53+
val signerKey = if (parameters.oidcClient.isPresent) parameters.oidcClient.get().key() else DEFAULT_KEY
54+
val signer = clients.computeIfAbsent(signerKey) {
5255
KeylessSigner.builder().apply {
5356
sigstorePublicDefaults()
54-
oidcClients(OidcClients.of(oidcClient.build() as OidcClient))
57+
if (signerKey != DEFAULT_KEY) {
58+
forceCredentialProviders(OidcClients.of(parameters.oidcClient.get().build() as OidcClient))
59+
}
5560
}.build()
5661
}
5762

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/test/kotlin/dev/sigstore/gradle/OidcDslTest.kt

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class OidcDslTest: BaseGradleTest() {
2727
fun `configure GitHub OIDC client explicitly`(gradle: TestedGradle) {
2828
writeBuildGradle(
2929
"""
30+
import dev.sigstore.sign.GitHubActionsOidc
31+
3032
plugins {
3133
id("java")
3234
id("dev.sigstore.sign-base")
@@ -35,19 +37,48 @@ class OidcDslTest: BaseGradleTest() {
3537
sigstoreSign {
3638
oidcClient {
3739
gitHub()
40+
client.set(gitHub)
3841
}
3942
}
40-
tasks.create('printConfig') {
43+
tasks.create('checkConfig') {
4144
def oidcClient = project.sigstoreSign.oidcClient.client
4245
doLast {
46+
if (!oidcClient.isPresent()) {
47+
throw GradleException("oidc client was unexpectadly not present")
48+
}
4349
println("s: ${'$'}{oidcClient.get()}")
4450
}
4551
}
4652
""".trimIndent()
4753
)
4854
enableConfigurationCache(gradle)
4955
enableProjectIsolation(gradle)
50-
prepare(gradle.version, "printConfig", "-s")
56+
prepare(gradle.version, "checkConfig", "-s")
57+
.build()
58+
}
59+
@ParameterizedTest
60+
@MethodSource("gradleVersionAndSettings")
61+
fun `unconfigured GitHub OIDC client is empty`(gradle: TestedGradle) {
62+
writeBuildGradle(
63+
"""
64+
plugins {
65+
id("java")
66+
id("dev.sigstore.sign-base")
67+
}
68+
group = "dev.sigstore.test"
69+
tasks.create('checkConfig') {
70+
def oidcClient = project.sigstoreSign.oidcClient.client
71+
doLast {
72+
if (oidcClient.isPresent()) {
73+
throw GradleException("gradle specific oidc client configured, when it should be delegating to lib:sigstore-java")
74+
}
75+
}
76+
}
77+
""".trimIndent()
78+
)
79+
enableConfigurationCache(gradle)
80+
enableProjectIsolation(gradle)
81+
prepare(gradle.version, "checkConfig", "-s")
5182
.build()
5283
}
5384
}

sigstore-gradle/sigstore-gradle-sign-base-plugin/src/test/kotlin/dev/sigstore/gradle/PluginSmokeTest.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,9 @@ class PluginSmokeTest : BaseGradleTest() {
6161
oidcClient {
6262
// Note: the code is red in IDEA because it does not understand
6363
// there's sam-with-receiver plugin for Action<.>
64-
gitHub {
65-
audience.set("sigstore-test")
66-
}
64+
gitHub {}
6765
web {
68-
clientId.set("sigstore-test")
66+
issuer.set("https://sigstore-test.example.com")
6967
}
7068
}
7169
}

sigstore-java/src/main/java/dev/sigstore/KeylessSigner.java

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@
5454
import dev.sigstore.timestamp.client.TimestampResponse;
5555
import dev.sigstore.timestamp.client.TimestampVerificationException;
5656
import dev.sigstore.timestamp.client.TimestampVerifier;
57+
import dev.sigstore.trustroot.LegacySigningConfig;
58+
import dev.sigstore.trustroot.Service;
5759
import dev.sigstore.trustroot.SigstoreConfigurationException;
5860
import dev.sigstore.tuf.SigstoreTufClient;
5961
import java.io.IOException;
60-
import java.net.URI;
6162
import java.nio.charset.StandardCharsets;
6263
import java.nio.file.Path;
6364
import java.security.InvalidAlgorithmParameterException;
@@ -160,40 +161,39 @@ public static Builder builder() {
160161

161162
public static class Builder {
162163
private TrustedRootProvider trustedRootProvider;
164+
private SigningConfigProvider signingConfigProvider;
163165
private OidcClients oidcClients;
164166
private List<OidcTokenMatcher> oidcIdentities = Collections.emptyList();
165167
private Signer signer;
166168
private Duration minSigningCertificateLifetime = DEFAULT_MIN_SIGNING_CERTIFICATE_LIFETIME;
167-
private URI fulcioUri;
168-
private URI rekorUri;
169-
private URI timestampUri;
170169

171170
@CanIgnoreReturnValue
172-
public Builder fulcioUrl(URI uri) {
173-
this.fulcioUri = uri;
174-
return this;
175-
}
176-
177-
@CanIgnoreReturnValue
178-
public Builder rekorUrl(URI uri) {
179-
this.rekorUri = uri;
171+
public Builder trustedRootProvider(TrustedRootProvider trustedRootProvider) {
172+
this.trustedRootProvider = trustedRootProvider;
180173
return this;
181174
}
182175

183176
@CanIgnoreReturnValue
184-
public Builder timestampUrl(URI uri) {
185-
this.timestampUri = uri;
177+
public Builder signingConfigProvider(SigningConfigProvider signingConfigProvider) {
178+
this.signingConfigProvider = signingConfigProvider;
186179
return this;
187180
}
188181

189-
@CanIgnoreReturnValue
190-
public Builder trustedRootProvider(TrustedRootProvider trustedRootProvider) {
191-
this.trustedRootProvider = trustedRootProvider;
192-
return this;
182+
/**
183+
* Deprecated, use {@link #forceCredentialProviders}. sigstore-gradle requires a one version
184+
* deprecation window, so keep this in here until we've done another release.
185+
*/
186+
@Deprecated(forRemoval = true)
187+
public Builder oidcClients(OidcClients oidcClients) {
188+
return forceCredentialProviders(oidcClients);
193189
}
194190

191+
/**
192+
* Override the default set of credential providers (ambient + signingConfig). It should be very
193+
* unusual for anyone to override this outside of testing scenarios.
194+
*/
195195
@CanIgnoreReturnValue
196-
public Builder oidcClients(OidcClients oidcClients) {
196+
public Builder forceCredentialProviders(OidcClients oidcClients) {
197197
this.oidcClients = oidcClients;
198198
return this;
199199
}
@@ -244,22 +244,51 @@ public KeylessSigner build()
244244
SigstoreConfigurationException {
245245
Preconditions.checkNotNull(trustedRootProvider);
246246
var trustedRoot = trustedRootProvider.get();
247-
Preconditions.checkNotNull(fulcioUri);
248-
Preconditions.checkNotNull(rekorUri);
249-
Preconditions.checkNotNull(oidcClients);
247+
Preconditions.checkNotNull(signingConfigProvider);
248+
var signingConfig = signingConfigProvider.get();
250249
Preconditions.checkNotNull(oidcIdentities);
251250
Preconditions.checkNotNull(signer);
252251
Preconditions.checkNotNull(minSigningCertificateLifetime);
253-
var fulcioClient = FulcioClientGrpc.builder().setUri(fulcioUri).build();
252+
var fulcioService = Service.select(signingConfig.getCas(), List.of(1));
253+
if (fulcioService.isEmpty()) {
254+
throw new SigstoreConfigurationException(
255+
"No suitable fulcio target was found in signing config");
256+
}
257+
var fulcioClient = FulcioClientGrpc.builder().setService(fulcioService.get()).build();
254258
var fulcioVerifier = FulcioVerifier.newFulcioVerifier(trustedRoot);
255-
var rekorClient = RekorClientHttp.builder().setUri(rekorUri).build();
259+
260+
var rekorService = Service.select(signingConfig.getTLogs(), List.of(1));
261+
if (rekorService.isEmpty()) {
262+
throw new SigstoreConfigurationException(
263+
"No suitable rekor target was found in signing config");
264+
}
265+
var rekorClient = RekorClientHttp.builder().setService(rekorService.get()).build();
256266
var rekorVerifier = RekorVerifier.newRekorVerifier(trustedRoot);
267+
257268
TimestampClient timestampClient = null;
258269
TimestampVerifier timestampVerifier = null;
259-
if (timestampUri != null) { // Building with staging defaults
260-
timestampClient = TimestampClientHttp.builder().setUri(timestampUri).build();
270+
var timestampService = Service.select(signingConfig.getTsas(), List.of(1));
271+
if (timestampService.isEmpty()) {
272+
if (rekorService.get().getApiVersion() != 1) {
273+
// only throw exception for rekor v2+ which will require time
274+
throw new SigstoreConfigurationException(
275+
"No suitable tsa target was found in signing config");
276+
}
277+
} else {
278+
timestampClient = TimestampClientHttp.builder().setService(timestampService.get()).build();
261279
timestampVerifier = TimestampVerifier.newTimestampVerifier(trustedRoot);
262280
}
281+
282+
// if the client hasn't overridden the oidc provider, determine it from the service config
283+
if (oidcClients == null) {
284+
var oidcService = Service.select(signingConfig.getOidcProviders(), List.of(1));
285+
if (oidcService.isEmpty()) {
286+
throw new SigstoreConfigurationException(
287+
"No suitable oidc target was found in signing config");
288+
}
289+
oidcClients = OidcClients.from(oidcService.get());
290+
}
291+
263292
return new KeylessSigner(
264293
fulcioClient,
265294
fulcioVerifier,
@@ -281,9 +310,10 @@ public KeylessSigner build()
281310
public Builder sigstorePublicDefaults() {
282311
var sigstoreTufClientBuilder = SigstoreTufClient.builder().usePublicGoodInstance();
283312
trustedRootProvider = TrustedRootProvider.from(sigstoreTufClientBuilder);
284-
fulcioUri = FulcioClient.PUBLIC_GOOD_URI;
285-
rekorUri = RekorClient.PUBLIC_GOOD_URI;
286-
oidcClients(OidcClients.PUBLIC_GOOD);
313+
// TODO: signing config is not pushed to prod yet
314+
signingConfigProvider =
315+
SigningConfigProvider.fromOrDefault(
316+
sigstoreTufClientBuilder, LegacySigningConfig.PUBLIC_GOOD);
287317
signer(Signers.newEcdsaSigner());
288318
minSigningCertificateLifetime(DEFAULT_MIN_SIGNING_CERTIFICATE_LIFETIME);
289319
return this;
@@ -297,10 +327,7 @@ public Builder sigstorePublicDefaults() {
297327
public Builder sigstoreStagingDefaults() {
298328
var sigstoreTufClientBuilder = SigstoreTufClient.builder().useStagingInstance();
299329
trustedRootProvider = TrustedRootProvider.from(sigstoreTufClientBuilder);
300-
fulcioUri = FulcioClient.STAGING_URI;
301-
rekorUri = RekorClient.STAGING_URI;
302-
timestampUri = TimestampClient.STAGING_URI;
303-
oidcClients(OidcClients.STAGING);
330+
signingConfigProvider = SigningConfigProvider.from(sigstoreTufClientBuilder);
304331
signer(Signers.newEcdsaSigner());
305332
minSigningCertificateLifetime(DEFAULT_MIN_SIGNING_CERTIFICATE_LIFETIME);
306333
return this;

sigstore-java/src/main/java/dev/sigstore/KeylessVerifier.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,11 @@ public static class Builder {
8989
private TrustedRootProvider trustedRootProvider;
9090

9191
public KeylessVerifier build()
92-
throws SigstoreConfigurationException,
93-
InvalidAlgorithmParameterException,
92+
throws InvalidAlgorithmParameterException,
9493
CertificateException,
9594
InvalidKeySpecException,
96-
NoSuchAlgorithmException {
95+
NoSuchAlgorithmException,
96+
SigstoreConfigurationException {
9797
Preconditions.checkNotNull(trustedRootProvider);
9898
var trustedRoot = trustedRootProvider.get();
9999
var fulcioVerifier = FulcioVerifier.newFulcioVerifier(trustedRoot);

0 commit comments

Comments
 (0)