Add support for untrusted CNB builders

A `trustBuilder` configuration option has been added to the Maven and
Gradle CNB integration image building goal and task. A known set of
builders published by Paketo, Heroku, and Google are trusted by default,
all other builders are untrusted by default.

Closes gh-41352
This commit is contained in:
Scott Frederick
2024-07-11 11:43:38 -05:00
parent c848a5e3ed
commit 224b06982e
62 changed files with 1654 additions and 207 deletions

View File

@@ -78,6 +78,23 @@ class BootBuildImageIntegrationTests {
String projectName = this.gradleBuild.getProjectDir().getName();
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
assertThat(result.getOutput()).contains("Running detector");
assertThat(result.getOutput()).contains("Running builder");
assertThat(result.getOutput()).contains("---> Test Info buildpack building");
assertThat(result.getOutput()).contains("Network status: HTTP/2 200");
assertThat(result.getOutput()).contains("---> Test Info buildpack done");
removeImages(projectName);
}
@TestTemplate
void buildsImageWithTrustBuilder() throws IOException {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.build("bootBuildImage");
String projectName = this.gradleBuild.getProjectDir().getName();
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
assertThat(result.getOutput()).contains("Running creator");
assertThat(result.getOutput()).contains("---> Test Info buildpack building");
assertThat(result.getOutput()).contains("Network status: HTTP/2 200");
assertThat(result.getOutput()).contains("---> Test Info buildpack done");
@@ -146,10 +163,11 @@ class BootBuildImageIntegrationTests {
writeLongNameResource();
BuildResult result = this.gradleBuild.build("bootBuildImage", "--pullPolicy=IF_NOT_PRESENT",
"--imageName=example/test-image-cmd", "--builder=ghcr.io/spring-io/spring-boot-cnb-test-builder:0.0.1",
"--runImage=paketobuildpacks/run-jammy-tiny", "--createdDate=2020-07-01T12:34:56Z",
"--trustBuilder", "--runImage=paketobuildpacks/run-jammy-tiny", "--createdDate=2020-07-01T12:34:56Z",
"--applicationDirectory=/application");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("example/test-image-cmd");
assertThat(result.getOutput()).contains("Running creator");
assertThat(result.getOutput()).contains("---> Test Info buildpack building");
assertThat(result.getOutput()).contains("---> Test Info buildpack done");
Image image = new DockerApi().image().inspect(ImageReference.of("example/test-image-cmd"));

View File

@@ -0,0 +1,14 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
if (project.hasProperty('applyWarPlugin')) {
apply plugin: 'war'
}
bootBuildImage {
builder = "ghcr.io/spring-io/spring-boot-cnb-test-builder:0.0.1"
trustBuilder = true
pullPolicy = "IF_NOT_PRESENT"
}

View File

@@ -118,9 +118,14 @@ The following table summarizes the available properties and their default values
| `builder`
| `--builder`
| Name of the Builder image to use.
| Name of the builder image to use.
| `paketobuildpacks/builder-jammy-tiny:latest`
| `trustBuilder`
| `--trustBuilder`
| Whether to treat the builder as https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/concepts/trusted_builders/#what-is-a-trusted-builder[trusted].
| `true` if the builder is one of `paketobuildpacks/builder-jammy-tiny`, `paketobuildpacks/builder-jammy-base`, `paketobuildpacks/builder-jammy-full`, `paketobuildpacks/builder-jammy-buildpackless-tiny`, `paketobuildpacks/builder-jammy-buildpackless-base`, `paketobuildpacks/builder-jammy-buildpackless-full`, `gcr.io/buildpacks/builder`, `heroku/builder`; false otherwise.
| `runImage`
| `--runImage`
| Name of the run image to use.

View File

@@ -91,6 +91,7 @@ public abstract class BootBuildImage extends DefaultTask {
}
return ImageReference.of(imageName, projectVersion.get()).toString();
}));
getTrustBuilder().convention((Boolean) null);
getCleanCache().convention(false);
getVerboseLogging().convention(false);
getPublish().convention(false);
@@ -131,6 +132,16 @@ public abstract class BootBuildImage extends DefaultTask {
@Option(option = "builder", description = "The name of the builder image to use")
public abstract Property<String> getBuilder();
/**
* Whether to treat the builder as trusted.
* @return whether to trust the builder
* @since 3.4.0
*/
@Input
@Optional
@Option(option = "trustBuilder", description = "Consider the builder trusted")
public abstract Property<Boolean> getTrustBuilder();
/**
* Returns the run image that will be included in the built image. When {@code null},
* the run image bundled with the builder will be used.
@@ -348,13 +359,16 @@ public abstract class BootBuildImage extends DefaultTask {
private BuildRequest customize(BuildRequest request) {
request = customizeBuilder(request);
if (getTrustBuilder().isPresent()) {
request = request.withTrustBuilder(getTrustBuilder().get());
}
request = customizeRunImage(request);
request = customizeEnvironment(request);
request = customizeCreator(request);
request = request.withCleanCache(getCleanCache().get());
request = request.withVerboseLogging(getVerboseLogging().get());
request = customizePullPolicy(request);
request = customizePublish(request);
request = request.withPublish(getPublish().get());
request = customizeBuildpacks(request);
request = customizeBindings(request);
request = customizeTags(request);
@@ -406,11 +420,6 @@ public abstract class BootBuildImage extends DefaultTask {
return request;
}
private BuildRequest customizePublish(BuildRequest request) {
request = request.withPublish(getPublish().get());
return request;
}
private BuildRequest customizeBuildpacks(BuildRequest request) {
List<String> buildpacks = getBuildpacks().getOrNull();
if (!CollectionUtils.isEmpty(buildpacks)) {

View File

@@ -172,14 +172,24 @@ class BootBuildImageTests {
@Test
void whenNoBuilderIsConfiguredThenRequestHasDefaultBuilder() {
assertThat(this.buildImage.createRequest().getBuilder().getName())
.isEqualTo("paketobuildpacks/builder-jammy-tiny");
BuildRequest request = this.buildImage.createRequest();
assertThat(request.getBuilder().getName()).isEqualTo("paketobuildpacks/builder-jammy-tiny");
assertThat(request.isTrustBuilder()).isTrue();
}
@Test
void whenBuilderIsConfiguredThenRequestUsesSpecifiedBuilder() {
this.buildImage.getBuilder().set("example.com/test/builder:1.2");
assertThat(this.buildImage.createRequest().getBuilder().getName()).isEqualTo("test/builder");
BuildRequest request = this.buildImage.createRequest();
assertThat(request.getBuilder().getName()).isEqualTo("test/builder");
assertThat(request.isTrustBuilder()).isFalse();
}
@Test
void whenTrustBuilderIsEnabledThenRequestHasTrustBuilderEnabled() {
this.buildImage.getBuilder().set("example.com/test/builder:1.2");
this.buildImage.getTrustBuilder().set(true);
assertThat(this.buildImage.createRequest().isTrustBuilder()).isTrue();
}
@Test