Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
Fix #2714: Allow setting security context of pods using persistent vo…
Browse files Browse the repository at this point in the history
…lumes
  • Loading branch information
k-wall committed Dec 4, 2019
1 parent 539d4f7 commit 065afb4
Show file tree
Hide file tree
Showing 25 changed files with 193 additions and 111 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 0.31.0
* #2714: Allow setting security context of pods using persistent volumes

## 0.30.0
* Fix go tool vet on newer versions of go
* #3247: Fix path to probe endpoint used when running on Kubernetes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,22 @@
import java.nio.file.Files;
import java.util.*;

import static io.enmasse.address.model.KubeUtil.applyPodTemplate;
import static io.enmasse.address.model.KubeUtil.lookupResource;
import static io.enmasse.address.model.KubeUtil.*;

public class TemplateInfraResourceFactory implements InfraResourceFactory {
private static final String FS_GROUP_OVERRIDE = "FS_GROUP_OVERRIDE";
private static final Logger log = LoggerFactory.getLogger(TemplateInfraResourceFactory.class);
private final String WELL_KNOWN_CONSOLE_SERVICE_NAME = "console";

private final Kubernetes kubernetes;
private final AuthenticationServiceResolver authenticationServiceResolver;
private final Map<String, String> env;
private final SchemaProvider schemaProvider;
private final Long fsGroupOverride;

public TemplateInfraResourceFactory(Kubernetes kubernetes, AuthenticationServiceResolver authenticationServiceResolver, Map<String, String> env, SchemaProvider schemaProvider) {
this.kubernetes = kubernetes;
this.authenticationServiceResolver = authenticationServiceResolver;
this.env = env;
this.schemaProvider = schemaProvider;
this.fsGroupOverride = getFsGroupOverride();
}

private void prepareParameters(AddressSpace addressSpace,
Expand Down Expand Up @@ -184,10 +180,7 @@ private List<HasMetadata> createStandardInfra(AddressSpace addressSpace, Standar
setIfEnvPresent(parameters, TemplateParameter.BROKER_PLUGIN_IMAGE);
setIfEnvPresent(parameters, TemplateParameter.TOPIC_FORWARDER_IMAGE);
setIfEnvPresent(parameters, TemplateParameter.IMAGE_PULL_POLICY);

if (fsGroupOverride != null) {
parameters.put(TemplateParameter.FS_GROUP_OVERRIDE, fsGroupOverride.toString());
}
setIfEnvPresent(parameters, TemplateParameter.FS_GROUP_FALLBACK_MAP);

Map<String, String> infraAnnotations = standardInfraConfig.getMetadata().getAnnotations();
String templateName = getAnnotation(infraAnnotations, AnnotationKeys.TEMPLATE_NAME, "standard-space-infra");
Expand Down Expand Up @@ -290,25 +283,11 @@ private List<HasMetadata> createBrokeredInfra(AddressSpace addressSpace, Brokere
PodTemplateSpec actualPodTemplate = brokerDeployment.getSpec().getTemplate();
applyPodTemplate(actualPodTemplate, podTemplate);
}

if (this.fsGroupOverride != null) {
KubeUtil.applyFsGroupOverride(Collections.singletonList(brokerDeployment), this.fsGroupOverride);
}
overrideFsGroup(brokerDeployment.getSpec().getTemplate(), "broker", kubernetes.getNamespace());

return items;
}

private Long getFsGroupOverride() {
Long fsGroupOverride = null;
if (env.containsKey(FS_GROUP_OVERRIDE)) {
try {
fsGroupOverride = Long.parseLong(env.get(FS_GROUP_OVERRIDE));
} catch (NumberFormatException ignore) {
}
}
return fsGroupOverride;
}

private List<HasMetadata> applyStorageClassName(String storageClassName, List<HasMetadata> items) {
if (storageClassName != null) {
for (HasMetadata item : items) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ public interface TemplateParameter {
String CONSOLE_OAUTH_DISCOVERY_URL = "CONSOLE_OAUTH_DISCOVERY_URL";
String CONSOLE_OAUTH_SCOPE = "CONSOLE_OAUTH_SCOPE";
String CONSOLE_LINK = "CONSOLE_LINK";
String FS_GROUP_OVERRIDE = "FS_GROUP_OVERRIDE";
String FS_GROUP_FALLBACK_MAP = "FS_GROUP_FALLBACK_MAP";
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ objects:
value: ${TOPIC_FORWARDER_IMAGE}
- name: IMAGE_PULL_POLICY
value: ${IMAGE_PULL_POLICY}
- name: FS_GROUP_OVERRIDE
value: ${FS_GROUP_OVERRIDE}
- name: FS_GROUP_FALLBACK_MAP
value: ${FS_GROUP_FALLBACK_MAP}
image: ${STANDARD_CONTROLLER_IMAGE}
imagePullPolicy: ${IMAGE_PULL_POLICY}
livenessProbe:
Expand Down Expand Up @@ -608,4 +608,4 @@ parameters:
- description:
name: CONSOLE_LINK
- description:
name: FS_GROUP_OVERRIDE
name: FS_GROUP_FALLBACK_MAP
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2019, EnMasse authors.
* License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
*/

package io.enmasse.address.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Map;

public class FsGroupFallback {

private static TypeReference<Map<String, Long>> MAP_STRING_LONG = new TypeReference<>() {
};

private static final Map<String, Long> FS_GROUP_FALLBACK;

static {

final String json = System.getenv().getOrDefault("FS_GROUP_FALLBACK_MAP", "{}");
ObjectMapper objectMapper = new ObjectMapper();
try {

FS_GROUP_FALLBACK = objectMapper.readValue(json, MAP_STRING_LONG);
} catch (JsonProcessingException e) {
throw new ExceptionInInitializerError(e);
}
}

public static Long getFsGroupOverride(String component) {
return FS_GROUP_FALLBACK.get(component);
}
}
39 changes: 17 additions & 22 deletions api-model/src/main/java/io/enmasse/address/model/KubeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@
import io.fabric8.kubernetes.api.model.PodSpec;
import io.fabric8.kubernetes.api.model.PodTemplateSpec;
import io.fabric8.kubernetes.api.model.Probe;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.StatefulSet;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -195,6 +192,10 @@ public static void applyPodTemplate(PodTemplateSpec actual, PodTemplateSpec desi
actualPodSpec.setTolerations(podSpec.getTolerations());
}

if (podSpec.getSecurityContext() != null) {
actualPodSpec.setSecurityContext(podSpec.getSecurityContext());
}

for (Container desiredContainer : podSpec.getInitContainers()) {
for (Container actualContainer : actualPodSpec.getInitContainers()) {
if (actualContainer.getName() != null && actualContainer.getName().equals(desiredContainer.getName())) {
Expand Down Expand Up @@ -291,23 +292,17 @@ public static boolean isNameValid(final String name) {
}
}

public static void applyFsGroupOverride(List<HasMetadata> items, Long fsGroupOverride) {
items.stream().filter(i -> i instanceof Deployment || i instanceof StatefulSet).forEach(
i -> {
PodSpec spec;
if (i instanceof StatefulSet) {
spec = ((StatefulSet) i).getSpec().getTemplate().getSpec();
} else {
spec = ((Deployment) i).getSpec().getTemplate().getSpec();
}

PodSecurityContext securityContext = new PodSecurityContext();
if (spec.getSecurityContext() != null) {
securityContext = spec.getSecurityContext();
}
securityContext.setFsGroup(fsGroupOverride);
spec.setSecurityContext(securityContext);
}
);
public static void overrideFsGroup(PodTemplateSpec target, String component, String namespace) {
final Long fsGroupOverride = FsGroupFallback.getFsGroupOverride(component);
if (Objects.equals(namespace, "openshift-operators") && fsGroupOverride != null) {
PodSecurityContext securityContext = target.getSpec().getSecurityContext();
if (securityContext == null) {
securityContext = new PodSecurityContext();
}
if (securityContext.getFsGroup() == null) {
securityContext.setFsGroup(fsGroupOverride);
target.getSpec().setSecurityContext(securityContext);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.fabric8.kubernetes.api.model.Doneable;
import io.fabric8.kubernetes.api.model.PodSecurityContext;
import io.fabric8.kubernetes.api.model.SecretReference;
import io.sundr.builder.annotations.Buildable;
import io.sundr.builder.annotations.BuildableReference;
Expand All @@ -23,12 +24,13 @@
},
inline = @Inline(type = Doneable.class, prefix = "Doneable", value = "done")
)
@JsonPropertyOrder({"credentialsSecret", "certificateSecret", "storage"})
@JsonPropertyOrder({"credentialsSecret", "certificateSecret", "storage", "securityContext"})
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AuthenticationServiceSpecStandard extends AbstractWithAdditionalProperties {
private SecretReference credentialsSecret;
private SecretReference certificateSecret;
private AuthenticationServiceSpecStandardStorage storage;
private PodSecurityContext securityContext;

public SecretReference getCredentialsSecret() {
return credentialsSecret;
Expand All @@ -45,12 +47,13 @@ public boolean equals(Object o) {
AuthenticationServiceSpecStandard that = (AuthenticationServiceSpecStandard) o;
return Objects.equals(credentialsSecret, that.credentialsSecret) &&
Objects.equals(certificateSecret, that.certificateSecret) &&
Objects.equals(storage, that.storage);
Objects.equals(storage, that.storage) &&
Objects.equals(securityContext, that.securityContext);
}

@Override
public int hashCode() {
return Objects.hash(credentialsSecret, certificateSecret, storage);
return Objects.hash(credentialsSecret, certificateSecret, storage, securityContext);
}

@Override
Expand All @@ -59,6 +62,7 @@ public String toString() {
"credentialsSecret=" + credentialsSecret +
", certificateSecret=" + certificateSecret +
", storage=" + storage +
", securityContext=" + securityContext +
'}';
}

Expand All @@ -77,4 +81,12 @@ public AuthenticationServiceSpecStandardStorage getStorage() {
public void setStorage(AuthenticationServiceSpecStandardStorage storage) {
this.storage = storage;
}

public PodSecurityContext getSecurityContext() {
return securityContext;
}

public void setSecurityContext(PodSecurityContext securityContext) {
this.securityContext = securityContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ public void appliesContainerOrderIgnored() {
assertThat(fooContainer.getResources(), equalTo(resources));
}

@Test
public void appliesSecurityContext() {

PodTemplateSpec actual = new PodTemplateSpecBuilder()
.withNewSpec()
.endSpec()
.build();

final long runAsGroup = 1000L;
final long fsGroup = 1234L;
PodTemplateSpec desired = new PodTemplateSpecBuilder()
.withNewSpec()
.withNewSecurityContext()
.withRunAsGroup(runAsGroup)
.withFsGroup(fsGroup)
.endSecurityContext()
.endSpec()
.build();

KubeUtil.applyPodTemplate(actual, desired);

assertThat(actual.getSpec().getSecurityContext().getFsGroup(), equalTo(fsGroup));
assertThat(actual.getSpec().getSecurityContext().getRunAsGroup(), equalTo(runAsGroup));
}

@Test
public void appliesContainerEnvVarToPodTemplate() {
Container actualContainer = new ContainerBuilder()
Expand Down
3 changes: 3 additions & 0 deletions api-server/src/main/resources/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@
"priorityClassName": {
"type": "string"
},
"securityContext": {
"type": "object"
},
"tolerations": {
"items": {
"type": "object"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,8 @@ spec:
value: "${env.CONSOLE_PROXY_KUBERNETES_IMAGE}"
- name: CONSOLE_HTTPD_IMAGE
value: "${env.CONSOLE_HTTPD_IMAGE}"
- name: FS_GROUP_STANDARD_AUTHSERVICE_OVERRIDE
value: "1000"
- name: "FS_GROUP_FALLBACK_MAP"
value: "{\"standard-authservice\": 1000}"
- name: user-api-server
spec:
replicas: 1
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/admin/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type AuthenticationServiceSpecStandard struct {
Resources *corev1.ResourceRequirements `json:"resources,omitempty"`
Storage *AuthenticationServiceSpecStandardStorage `json:"storage,omitempty"`
Datasource *AuthenticationServiceSpecStandardDatasource `json:"datasource,omitempty"`
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
}

type StorageType string
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/admin/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/apis/iot/v1alpha1/types_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ type AuthenticationServiceConfig struct {
type FileBasedDeviceRegistry struct {
NumberOfDevicesPerTenant *uint32 `json:"numberOfDevicesPerTenant,omitempty"`
CommonServiceConfig `json:",inline"`
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
}

type InfinispanDeviceRegistry struct {
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/iot/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions pkg/controller/address_space_controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,8 @@ func ApplyDeployment(deployment *appsv1.Deployment) error {
install.ApplyEnvSimple(container, "ENABLE_EVENT_LOGGER", "true")
install.ApplyEnvSimple(container, "TEMPLATE_DIR", "/opt/templates")
install.ApplyEnvSimple(container, "RESOURCES_DIR", "/opt")

value, ok := os.LookupEnv("FS_GROUP_OVERRIDE")
if ok {
install.ApplyEnvSimple(container, "FS_GROUP_OVERRIDE", value)
if value, ok := os.LookupEnv("FS_GROUP_FALLBACK_MAP"); ok {
install.ApplyOrRemoveEnvSimple(container, "FS_GROUP_FALLBACK_MAP", value)
}

t := true
Expand Down
10 changes: 1 addition & 9 deletions pkg/controller/authenticationservice/standard.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,7 @@ func applyStandardAuthServiceDeployment(authservice *adminv1beta1.Authentication

install.ApplyDeploymentDefaults(deployment, "standard-authservice", *authservice.Spec.Standard.DeploymentName)

err := install.ApplyFsGroupOverride("FS_GROUP_OVERRIDE", deployment)
if err != nil {
return err
}

err = install.ApplyFsGroupOverride("FS_GROUP_STANDARD_AUTHSERVICE_OVERRIDE", deployment)
if err != nil {
return err
}
install.OverrideSecurityContextFsGroup("standard-authservice", authservice.Spec.Standard.SecurityContext, deployment)

if err := install.ApplyInitContainerWithError(deployment, "keycloak-plugin", func(container *corev1.Container) error {
if err := install.ApplyContainerImage(container, "keycloak-plugin", authservice.Spec.Standard.InitImage); err != nil {
Expand Down
Loading

0 comments on commit 065afb4

Please sign in to comment.