Add support for security options in CNB builder container config

Closes gh-37479
This commit is contained in:
Scott Frederick
2023-09-20 13:30:20 -05:00
parent 4433fcd1f2
commit 7de770f6a1
15 changed files with 273 additions and 21 deletions

View File

@@ -87,6 +87,8 @@ public class BuildRequest {
private final String applicationDirectory;
private final List<String> securityOptions;
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(applicationContent, "ApplicationContent must not be null");
@@ -109,13 +111,14 @@ public class BuildRequest {
this.launchCache = null;
this.createdDate = null;
this.applicationDirectory = null;
this.securityOptions = null;
}
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List<BuildpackReference> buildpacks,
List<Binding> bindings, String network, List<ImageReference> tags, Cache buildWorkspace, Cache buildCache,
Cache launchCache, Instant createdDate, String applicationDirectory) {
Cache launchCache, Instant createdDate, String applicationDirectory, List<String> securityOptions) {
this.name = name;
this.applicationContent = applicationContent;
this.builder = builder;
@@ -135,6 +138,7 @@ public class BuildRequest {
this.launchCache = launchCache;
this.createdDate = createdDate;
this.applicationDirectory = applicationDirectory;
this.securityOptions = securityOptions;
}
/**
@@ -147,7 +151,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, builder.inTaggedOrDigestForm(), this.runImage,
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
this.launchCache, this.createdDate, this.applicationDirectory);
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
}
/**
@@ -159,7 +163,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, runImageName.inTaggedOrDigestForm(),
this.creator, this.env, this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
this.launchCache, this.createdDate, this.applicationDirectory);
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
}
/**
@@ -172,7 +176,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -189,7 +193,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
Collections.unmodifiableMap(env), this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish,
this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace, this.buildCache,
this.launchCache, this.createdDate, this.applicationDirectory);
this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
}
/**
@@ -204,7 +208,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator,
Collections.unmodifiableMap(updatedEnv), this.cleanCache, this.verboseLogging, this.pullPolicy,
this.publish, this.buildpacks, this.bindings, this.network, this.tags, this.buildWorkspace,
this.buildCache, this.launchCache, this.createdDate, this.applicationDirectory);
this.buildCache, this.launchCache, this.createdDate, this.applicationDirectory, this.securityOptions);
}
/**
@@ -216,7 +220,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -228,7 +232,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -240,7 +244,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -252,7 +256,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -277,7 +281,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -302,7 +306,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -315,7 +319,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -338,7 +342,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -351,7 +355,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -364,7 +368,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, buildCache, this.launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -377,7 +381,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, launchCache, this.createdDate,
this.applicationDirectory);
this.applicationDirectory, this.securityOptions);
}
/**
@@ -390,7 +394,7 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache,
parseCreatedDate(createdDate), this.applicationDirectory);
parseCreatedDate(createdDate), this.applicationDirectory, this.securityOptions);
}
private Instant parseCreatedDate(String createdDate) {
@@ -415,7 +419,20 @@ public class BuildRequest {
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
applicationDirectory);
applicationDirectory, this.securityOptions);
}
/**
* Return a new {@link BuildRequest} with an updated security options.
* @param securityOptions the security options
* @return an updated build request
*/
public BuildRequest withSecurityOptions(List<String> securityOptions) {
Assert.notNull(securityOptions, "SecurityOption must not be null");
return new BuildRequest(this.name, this.applicationContent, this.builder, this.runImage, this.creator, this.env,
this.cleanCache, this.verboseLogging, this.pullPolicy, this.publish, this.buildpacks, this.bindings,
this.network, this.tags, this.buildWorkspace, this.buildCache, this.launchCache, this.createdDate,
this.applicationDirectory, securityOptions);
}
/**
@@ -571,6 +588,14 @@ public class BuildRequest {
return this.applicationDirectory;
}
/**
* Return the security options that should be used by the lifecycle.
* @return the security options
*/
public List<String> getSecurityOptions() {
return this.securityOptions;
}
/**
* Factory method to create a new {@link BuildRequest} from a JAR file.
* @param jarFile the source jar file

View File

@@ -20,6 +20,7 @@ import java.io.Closeable;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -58,6 +59,8 @@ class Lifecycle implements Closeable {
private static final String DOMAIN_SOCKET_PATH = "/var/run/docker.sock";
private static final List<String> DEFAULT_SECURITY_OPTIONS = List.of("label=disable");
private final BuildLog log;
private final DockerApi docker;
@@ -82,6 +85,8 @@ class Lifecycle implements Closeable {
private final String applicationDirectory;
private final List<String> securityOptions;
private boolean executed;
private boolean applicationVolumePopulated;
@@ -108,6 +113,7 @@ class Lifecycle implements Closeable {
this.buildCache = getBuildCache(request);
this.launchCache = getLaunchCache(request);
this.applicationDirectory = getApplicationDirectory(request);
this.securityOptions = getSecurityOptions(request);
}
private Cache getBuildCache(BuildRequest request) {
@@ -128,6 +134,13 @@ class Lifecycle implements Closeable {
return (request.getApplicationDirectory() != null) ? request.getApplicationDirectory() : Directory.APPLICATION;
}
private List<String> getSecurityOptions(BuildRequest request) {
if (request.getSecurityOptions() != null) {
return request.getSecurityOptions();
}
return (Platform.isWindows()) ? Collections.emptyList() : DEFAULT_SECURITY_OPTIONS;
}
private ApiVersion getPlatformVersion(BuilderMetadata.Lifecycle lifecycle) {
if (lifecycle.getApis().getPlatform() != null) {
String[] supportedVersions = lifecycle.getApis().getPlatform();
@@ -240,8 +253,8 @@ class Lifecycle implements Closeable {
else {
phase.withBinding(Binding.from(DOMAIN_SOCKET_PATH, DOMAIN_SOCKET_PATH));
}
if (!Platform.isWindows()) {
phase.withSecurityOption("label=disable");
if (this.securityOptions != null) {
this.securityOptions.forEach(phase::withSecurityOption);
}
}

View File

@@ -333,6 +333,13 @@ class BuildRequestTests {
assertThat(withAppDir.getApplicationDirectory()).isEqualTo("/application");
}
@Test
void withSecurityOptionsSetsSecurityOptions() throws Exception {
BuildRequest request = BuildRequest.forJarFile(writeTestJarFile("my-app-0.0.1.jar"));
BuildRequest withAppDir = request.withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
assertThat(withAppDir.getSecurityOptions()).containsExactly("label=user:USER", "label=role:ROLE");
}
private void hasExpectedJarContent(TarArchive archive) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

View File

@@ -23,6 +23,7 @@ import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -254,6 +255,17 @@ class LifecycleTests {
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithSecurityOptionsExecutesPhases() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().create(any(), any())).willAnswer(answerWithGeneratedContainerId());
given(this.docker.container().wait(any())).willReturn(ContainerStatus.of(0, null));
BuildRequest request = getTestRequest().withSecurityOptions(List.of("label=user:USER", "label=role:ROLE"));
createLifecycle(request).execute();
assertPhaseWasRun("creator", withExpectedConfig("lifecycle-creator-security-opts.json"));
assertThat(this.out.toString()).contains("Successfully built image 'docker.io/library/my-application:latest'");
}
@Test
void executeWithDockerHostAndRemoteAddressExecutesPhases() throws Exception {
given(this.docker.container().create(any())).willAnswer(answerWithGeneratedContainerId());

View File

@@ -0,0 +1,40 @@
{
"User": "root",
"Image": "pack.local/ephemeral-builder",
"Cmd": [
"/cnb/lifecycle/creator",
"-app",
"/workspace",
"-platform",
"/platform",
"-run-image",
"docker.io/cloudfoundry/run:latest",
"-layers",
"/layers",
"-cache-dir",
"/cache",
"-launch-cache",
"/launch-cache",
"-daemon",
"docker.io/library/my-application:latest"
],
"Env": [
"CNB_PLATFORM_API=0.8"
],
"Labels": {
"author": "spring-boot"
},
"HostConfig": {
"Binds": [
"/var/run/docker.sock:/var/run/docker.sock",
"pack-layers-aaaaaaaaaa:/layers",
"pack-app-aaaaaaaaaa:/workspace",
"pack-cache-b35197ac41ea.build:/cache",
"pack-cache-b35197ac41ea.launch:/launch-cache"
],
"SecurityOpt" : [
"label=user:USER",
"label=role:ROLE"
]
}
}