Skip to content

Commit 7de770f

Browse files
Add support for security options in CNB builder container config
Closes gh-37479
1 parent 4433fcd commit 7de770f

File tree

15 files changed

+273
-21
lines changed

15 files changed

+273
-21
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/BuildRequest.java

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public class BuildRequest {
8787

8888
private final String applicationDirectory;
8989

90+
private final List<String> securityOptions;
91+
9092
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
9193
Assert.notNull(name, "Name must not be null");
9294
Assert.notNull(applicationContent, "ApplicationContent must not be null");
@@ -109,13 +111,14 @@ public class BuildRequest {
109111
this.launchCache = null;
110112
this.createdDate = null;
111113
this.applicationDirectory = null;
114+
this.securityOptions = null;
112115
}
113116

114117
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
115118
ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
116119
boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List<BuildpackReference> buildpacks,
117120
List<Binding> bindings, String network, List<ImageReference> tags, Cache buildWorkspace, Cache buildCache,
118-
Cache launchCache, Instant createdDate, String applicationDirectory) {
121+
Cache launchCache, Instant createdDate, String applicationDirectory, List<String> securityOptions) {
119122
this.name = name;
120123
this.applicationContent = applicationContent;
121124
this.builder = builder;
@@ -135,6 +138,7 @@ public class BuildRequest {
135138
this.launchCache = launchCache;
136139
this.createdDate = createdDate;
137140
this.applicationDirectory = applicationDirectory;
141+
this.securityOptions = securityOptions;
138142
}
139143

140144
/**
@@ -147,7 +151,7 @@ public BuildRequest withBuilder(ImageReference builder) {
147151
return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage,
148152
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
149153
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
150-
this.launchCache, this.createdDate, this.applicationDirectory);
154+
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
151155
}
152156

153157
/**
@@ -159,7 +163,7 @@ public BuildRequest withRunImage(ImageReference runImageName) {
159163
return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(),
160164
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
161165
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
162-
this.launchCache, this.createdDate, this.applicationDirectory);
166+
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
163167
}
164168

165169
/**
@@ -172,7 +176,7 @@ public BuildRequest withCreator(Creator creator) {
172176
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env,
173177
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
174178
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
175-
this.applicationDirectory);
179+
this.applicationDirectory, this.securityOptions);
176180
}
177181

178182
/**
@@ -189,7 +193,7 @@ public BuildRequest withEnv(String name, String value) {
189193
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
190194
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
191195
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
192-
this.launchCache, this.createdDate, this.applicationDirectory);
196+
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
193197
}
194198

195199
/**
@@ -204,7 +208,7 @@ public BuildRequest withEnv(Map<String, String> env) {
204208
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
205209
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy,
206210
this.publish, this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace,
207-
this.buildCache, this.launchCache, this.createdDate, this.applicationDirectory);
211+
this.buildCache, this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
208212
}
209213

210214
/**
@@ -216,7 +220,7 @@ public BuildRequest withCleanCache(boolean cleanCache) {
216220
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
217221
cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
218222
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
219-
this.applicationDirectory);
223+
this.applicationDirectory, this.securityOptions);
220224
}
221225

222226
/**
@@ -228,7 +232,7 @@ public BuildRequest withVerboseLogging(boolean verboseLogging) {
228232
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
229233
this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
230234
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
231-
this.applicationDirectory);
235+
this.applicationDirectory, this.securityOptions);
232236
}
233237

234238
/**
@@ -240,7 +244,7 @@ public BuildRequest withPullPolicy(PullPolicy pullPolicy) {
240244
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
241245
this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings,
242246
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
243-
this.applicationDirectory);
247+
this.applicationDirectory, this.securityOptions);
244248
}
245249

246250
/**
@@ -252,7 +256,7 @@ public BuildRequest withPublish(boolean publish) {
252256
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
253257
this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings,
254258
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
255-
this.applicationDirectory);
259+
this.applicationDirectory, this.securityOptions);
256260
}
257261

258262
/**
@@ -277,7 +281,7 @@ public BuildRequest withBuildpacks(List<BuildpackReference> buildpacks) {
277281
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
278282
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings,
279283
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
280-
this.applicationDirectory);
284+
this.applicationDirectory, this.securityOptions);
281285
}
282286

283287
/**
@@ -302,7 +306,7 @@ public BuildRequest withBindings(List<Binding> bindings) {
302306
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
303307
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings,
304308
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
305-
this.applicationDirectory);
309+
this.applicationDirectory, this.securityOptions);
306310
}
307311

308312
/**
@@ -315,7 +319,7 @@ public BuildRequest withNetwork(String network) {
315319
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
316320
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
317321
network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
318-
this.applicationDirectory);
322+
this.applicationDirectory, this.securityOptions);
319323
}
320324

321325
/**
@@ -338,7 +342,7 @@ public BuildRequest withTags(List<ImageReference> tags) {
338342
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
339343
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
340344
this.network, tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
341-
this.applicationDirectory);
345+
this.applicationDirectory, this.securityOptions);
342346
}
343347

344348
/**
@@ -351,7 +355,7 @@ public BuildRequest withBuildWorkspace(Cache buildWorkspace) {
351355
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
352356
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
353357
this.network, this.tags, buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
354-
this.applicationDirectory);
358+
this.applicationDirectory, this.securityOptions);
355359
}
356360

357361
/**
@@ -364,7 +368,7 @@ public BuildRequest withBuildCache(Cache buildCache) {
364368
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
365369
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
366370
this.network, this.tags, this.buildWorkspace, buildCache, this.launchCache, this.createdDate,
367-
this.applicationDirectory);
371+
this.applicationDirectory, this.securityOptions);
368372
}
369373

370374
/**
@@ -377,7 +381,7 @@ public BuildRequest withLaunchCache(Cache launchCache) {
377381
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
378382
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
379383
this.network, this.tags, this.buildWorkspace, this.buildCache, launchCache, this.createdDate,
380-
this.applicationDirectory);
384+
this.applicationDirectory, this.securityOptions);
381385
}
382386

383387
/**
@@ -390,7 +394,7 @@ public BuildRequest withCreatedDate(String createdDate) {
390394
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
391395
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
392396
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache,
393-
parseCreatedDate(createdDate), this.applicationDirectory);
397+
parseCreatedDate(createdDate), this.applicationDirectory, this.securityOptions);
394398
}
395399

396400
private Instant parseCreatedDate(String createdDate) {
@@ -415,7 +419,20 @@ public BuildRequest withApplicationDirectory(String applicationDirectory) {
415419
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
416420
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
417421
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
418-
applicationDirectory);
422+
applicationDirectory, this.securityOptions);
423+
}
424+
425+
/**
426+
* Return a new {@link BuildRequest} with an updated security options.
427+
* @param securityOptions the security options
428+
* @return an updated build request
429+
*/
430+
public BuildRequest withSecurityOptions(List<String> securityOptions) {
431+
Assert.notNull(securityOptions, "SecurityOption must not be null");
432+
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
433+
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
434+
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
435+
this.applicationDirectory, securityOptions);
419436
}
420437

421438
/**
@@ -571,6 +588,14 @@ public String getApplicationDirectory() {
571588
return this.applicationDirectory;
572589
}
573590

591+
/**
592+
* Return the security options that should be used by the lifecycle.
593+
* @return the security options
594+
*/
595+
public List<String> getSecurityOptions() {
596+
return this.securityOptions;
597+
}
598+
574599
/**
575600
* Factory method to create a new {@link BuildRequest} from a JAR file.
576601
* @param jarFile the source jar file

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Lifecycle.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.nio.file.Files;
2222
import java.nio.file.Path;
23+
import java.util.Collections;
2324
import java.util.List;
2425
import java.util.function.Consumer;
2526

@@ -58,6 +59,8 @@ class Lifecycle implements Closeable {
5859

5960
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock";
6061

62+
private static final List<String> DEFAULT_SECURITY_OPTIONS = List.of("label=disable");
63+
6164
private final BuildLog log;
6265

6366
private final DockerApi docker;
@@ -82,6 +85,8 @@ class Lifecycle implements Closeable {
8285

8386
private final String applicationDirectory;
8487

88+
private final List<String> securityOptions;
89+
8590
private boolean executed;
8691

8792
private boolean applicationVolumePopulated;
@@ -108,6 +113,7 @@ class Lifecycle implements Closeable {
108113
this.buildCache = getBuildCache(request);
109114
this.launchCache = getLaunchCache(request);
110115
this.applicationDirectory = getApplicationDirectory(request);
116+
this.securityOptions = getSecurityOptions(request);
111117
}
112118

113119
private Cache getBuildCache(BuildRequest request) {
@@ -128,6 +134,13 @@ private String getApplicationDirectory(BuildRequest request) {
128134
return (request.getApplicationDirectory() != null) ? request.getApplicationDirectory() : Directory.APPLICATION;
129135
}
130136

137+
private List<String> getSecurityOptions(BuildRequest request) {
138+
if (request.getSecurityOptions() != null) {
139+
return request.getSecurityOptions();
140+
}
141+
return (Platform.isWindows()) ? Collections.emptyList() : DEFAULT_SECURITY_OPTIONS;
142+
}
143+
131144
private ApiVersion getPlatformVersion(BuilderMetadata.Lifecycle lifecycle) {
132145
if (lifecycle.getApis().getPlatform() != null) {
133146
String[] supportedVersions = lifecycle.getApis().getPlatform();
@@ -240,8 +253,8 @@ private void configureDaemonAccess(Phase phase) {
240253
else {
241254
phase.withBinding(Binding.from(DOMAIN_SOCKET_PATH, DOMAIN_SOCKET_PATH));
242255
}
243-
if (!Platform.isWindows()) {
244-
phase.withSecurityOption("label=disable");
256+
if (this.securityOptions != null) {
257+
this.securityOptions.forEach(phase::withSecurityOption);
245258
}
246259
}
247260

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/BuildRequestTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,13 @@ void withApplicationDirectorySetsApplicationDirectory() throws Exception {
333333
assertThat(withAppDir.getApplicationDirectory()).isEqualTo("/application");
334334
}
335335

336+
@Test
337+
void withSecurityOptionsSetsSecurityOptions() throws Exception {
338+
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
339+
BuildRequest withAppDir = request.withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
340+
assertThat(withAppDir.getSecurityOptions()).containsExactly("label=user:USER", "label=role:ROLE");
341+
}
342+
336343
private void hasExpectedJarContent(TarArchive archive) {
337344
try {
338345
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/LifecycleTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.io.PrintStream;
2424
import java.nio.charset.StandardCharsets;
2525
import java.util.LinkedHashMap;
26+
import java.util.List;
2627
import java.util.Map;
2728

2829
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -254,6 +255,17 @@ void executeWithApplicationDirectoryExecutesPhases() throws Exception {
254255
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
255256
}
256257

258+
@Test
259+
void executeWithSecurityOptionsExecutesPhases() throws Exception {
260+
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
261+
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
262+
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
263+
BuildRequest request = getTestRequest().withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
264+
createLifecycle(request).execute();
265+
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-security-opts.json"));
266+
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
267+
}
268+
257269
@Test
258270
void executeWithDockerHostAndRemoteAddressExecutesPhases() throws Exception {
259271
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"User": "root",
3+
"Image": "pack.local/ephemeral-builder",
4+
"Cmd": [
5+
"/cnb/lifecycle/creator",
6+
"-app",
7+
"/workspace",
8+
"-platform",
9+
"/platform",
10+
"-run-image",
11+
"docker.io/cloudfoundry/run:latest",
12+
"-layers",
13+
"/layers",
14+
"-cache-dir",
15+
"/cache",
16+
"-launch-cache",
17+
"/launch-cache",
18+
"-daemon",
19+
"docker.io/library/my-application:latest"
20+
],
21+
"Env": [
22+
"CNB_PLATFORM_API=0.8"
23+
],
24+
"Labels": {
25+
"author": "spring-boot"
26+
},
27+
"HostConfig": {
28+
"Binds": [
29+
"/var/run/docker.sock:/var/run/docker.sock",
30+
"pack-layers-aaaaaaaaaa:/layers",
31+
"pack-app-aaaaaaaaaa:/workspace",
32+
"pack-cache-b35197ac41ea.build:/cache",
33+
"pack-cache-b35197ac41ea.launch:/launch-cache"
34+
],
35+
"SecurityOpt" : [
36+
"label=user:USER",
37+
"label=role:ROLE"
38+
]
39+
}
40+
}

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/docs/asciidoc/packaging-oci-image.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ The value must be a string in the ISO 8601 instant format, or `now` to use the c
223223
Application contents will also be in this location in the generated image.
224224
| `/workspace`
225225

226+
| `securityOptions`
227+
| `--securityOptions`
228+
| https://docs.docker.com/engine/reference/run/#security-configuration[Security options] that will be applied to the builder container, provided as an array of string values
229+
| `["label=disable"]`
230+
226231
|===
227232

228233
NOTE: The plugin detects the target Java compatibility of the project using the JavaPlugin's `targetCompatibility` property.

0 commit comments

Comments
 (0)