Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-73513] Fix inheritance of container capabilities and and pod shareProcessNamespace settings #1592

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import hudson.model.Label;
import hudson.model.Node;
import hudson.slaves.NodeProperty;
import io.fabric8.kubernetes.api.model.Capabilities;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerBuilder;
import io.fabric8.kubernetes.api.model.EnvFromSource;
Expand Down Expand Up @@ -184,6 +185,7 @@ public static Container combine(@CheckForNull Container parent, @NonNull Contain
: (parent.getSecurityContext() != null
? parent.getSecurityContext().getRunAsGroup()
: null);
Capabilities capabilities = combineCapabilities(parent, template);
String imagePullPolicy = isNullOrEmpty(template.getImagePullPolicy())
? parent.getImagePullPolicy()
: template.getImagePullPolicy();
Expand Down Expand Up @@ -219,12 +221,13 @@ public static Container combine(@CheckForNull Container parent, @NonNull Contain
.withEnv(combineEnvVars(parent, template)) //
.withEnvFrom(combinedEnvFromSources(parent, template))
.withVolumeMounts(new ArrayList<>(volumeMounts.values()));
if ((privileged != null && privileged) || runAsUser != null || runAsGroup != null) {
if ((privileged != null && privileged) || runAsUser != null || runAsGroup != null || capabilities != null) {
containerBuilder = containerBuilder
.withNewSecurityContext()
.withNewSecurityContextLike(parent.getSecurityContext())
.withPrivileged(privileged)
.withRunAsUser(runAsUser)
.withRunAsGroup(runAsGroup)
.withCapabilities(capabilities)
.endSecurityContext();
}
return containerBuilder.build();
Expand All @@ -245,6 +248,41 @@ private static Map<String, Quantity> combineResources(
);
}

private static Capabilities combineCapabilities(Container parent, Container template) {
Capabilities parentCapabilities = parent.getSecurityContext() != null
? parent.getSecurityContext().getCapabilities()
: null;
Capabilities templateCapabilities = template.getSecurityContext() != null
? template.getSecurityContext().getCapabilities()
: null;
if (parentCapabilities == null && templateCapabilities == null) {
return null;
}
if (parentCapabilities == null) {
return templateCapabilities;
}
if (templateCapabilities == null) {
return parentCapabilities;
}
Capabilities combined = new Capabilities();
combined.setAdd(combineCapabilities(parentCapabilities, templateCapabilities, Capabilities::getAdd));
combined.setDrop(combineCapabilities(parentCapabilities, templateCapabilities, Capabilities::getDrop));
return combined;
}

private static List<String> combineCapabilities(
Capabilities parentCapabilities,
Capabilities templateCapabilities,
Function<Capabilities, List<String>> capabilitiesListFunction) {
List<String> parentCapabilitiesList = capabilitiesListFunction.apply(parentCapabilities);
List<String> templateCapabilitiesList = capabilitiesListFunction.apply(templateCapabilities);
// override: template capabilities win
if (templateCapabilitiesList != null) {
return templateCapabilitiesList;
}
return parentCapabilitiesList;
}

/**
* Combines all given pods together in order.
* @param pods the pods to combine
Expand Down Expand Up @@ -293,6 +331,10 @@ public static Pod combine(Pod parent, Pod template) {
? template.getSpec().getHostNetwork()
: parent.getSpec().getHostNetwork();

Boolean shareProcessNamespace = template.getSpec().getShareProcessNamespace() != null
? template.getSpec().getShareProcessNamespace()
: parent.getSpec().getShareProcessNamespace();

Map<String, String> podAnnotations = mergeMaps(
parent.getMetadata().getAnnotations(), template.getMetadata().getAnnotations());
Map<String, String> podLabels = mergeMaps(
Expand Down Expand Up @@ -346,6 +388,7 @@ public static Pod combine(Pod parent, Pod template) {
.withServiceAccountName(serviceAccountName) //
.withSchedulerName(schedulerName)
.withHostNetwork(hostNetwork) //
.withShareProcessNamespace(shareProcessNamespace) //
.withContainers(combinedContainers) //
.withInitContainers(combinedInitContainers) //
.withVolumes(combinedVolumes) //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -982,4 +982,137 @@ public void shouldSanitizeJenkinsLabel() {
"ylooooooooooooooooooooooooooooonglabelendinginunderscorex",
sanitizeLabel("label1 label2 verylooooooooooooooooooooooooooooonglabelendinginunderscore_"));
}

@Test
public void shouldCombineCapabilities() {
Container container1 = containerBuilder()
.withNewSecurityContext()
.withNewCapabilities()
.addToAdd("TO_ADD")
.withDrop((List<String>) null)
.endCapabilities()
.withRunAsUser(1000L)
.endSecurityContext()
.build();
Container container2 = containerBuilder()
.withNewSecurityContext()
.withNewCapabilities()
.addToDrop("TO_DROP")
.withAdd((List<String>) null)
.endCapabilities()
.endSecurityContext()
.build();
Container container3 = containerBuilder().build();

Container result = combine(container1, container3);
assertNotNull(result.getSecurityContext());
assertNotNull(result.getSecurityContext().getCapabilities());
assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD"));

result = combine(container3, container1);
assertNotNull(result.getSecurityContext());
assertNotNull(result.getSecurityContext().getCapabilities());
assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD"));

result = combine(container2, container3);
assertNotNull(result.getSecurityContext());
assertNotNull(result.getSecurityContext().getCapabilities());
assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("TO_DROP"));

result = combine(container1, container2);
assertNotNull(result.getSecurityContext());
assertNotNull(result.getSecurityContext().getCapabilities());
assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("TO_ADD"));
assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("TO_DROP"));
}

@Test
public void shouldOverrideCapabilitiesWithTemplate() {
Container container1 = containerBuilder()
.withNewSecurityContext()
.withNewCapabilities()
.addToAdd("CONTAINER1_ADD")
.addToDrop("CONTAINER1_DROP")
.endCapabilities()
.endSecurityContext()
.build();
Container container2 = containerBuilder()
.withNewSecurityContext()
.withNewCapabilities()
.addToAdd("CONTAINER2_ADD")
.addToDrop("CONTAINER2_DROP")
.endCapabilities()
.endSecurityContext()
.build();

Container result = combine(container1, container2);
assertNotNull(result.getSecurityContext());
assertNotNull(result.getSecurityContext().getCapabilities());
assertTrue(result.getSecurityContext().getCapabilities().getAdd().contains("CONTAINER2_ADD"));
assertTrue(result.getSecurityContext().getCapabilities().getDrop().contains("CONTAINER2_DROP"));
}

@Test
public void shouldRetainNullsWhenCombiningCapabilities() {

Container container1 = new ContainerBuilder().build();
Container container2 = new ContainerBuilder().build();
Container container3 = new ContainerBuilder()
.withNewSecurityContext()
.withPrivileged()
.endSecurityContext()
.build();

Container result = combine(container1, container2);
assertNull(result.getSecurityContext());

result = combine(container2, container3);
assertNotNull(result.getSecurityContext());
assertNull(result.getSecurityContext().getCapabilities());
}

@Test
public void shouldOverrideShareProcessNamespaceIfSpecified() {
Pod parent1 = new PodBuilder()
.withNewMetadata()
.endMetadata()
.withNewSpec()
.endSpec()
.build();

Pod parent2 = new PodBuilder()
.withNewMetadata()
.endMetadata()
.withNewSpec()
.withShareProcessNamespace()
.endSpec()
.build();

Pod child1 = new PodBuilder()
.withNewMetadata()
.endMetadata()
.withNewSpec()
.withShareProcessNamespace(false)
.endSpec()
.build();

Pod child2 = new PodBuilder()
.withNewMetadata()
.endMetadata()
.withNewSpec()
.endSpec()
.build();

Pod result1 = combine(parent1, child1);
assertFalse(result1.getSpec().getShareProcessNamespace());

Pod result2 = combine(parent1, child2);
assertNull(result2.getSpec().getShareProcessNamespace());

Pod result3 = combine(parent2, child1);
assertFalse(result3.getSpec().getShareProcessNamespace());

Pod result4 = combine(parent2, child2);
assertTrue(result4.getSpec().getShareProcessNamespace());
}
}