Skip to content

Commit

Permalink
SecurityContext added to ActiveGate container
Browse files Browse the repository at this point in the history
  • Loading branch information
aorcholski committed Feb 23, 2022
1 parent b67d1a9 commit 48b51a5
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 26 deletions.
12 changes: 12 additions & 0 deletions src/controllers/activegate/capability/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ package capability
const (
ActiveGateContainerName = "activegate"

ActiveGateVolumeNameGatewayConfig = "ag-lib-gateway-config"
ActiveGateVolumeNameGatewayTemp = "ag-lib-gateway-temp"
ActiveGateVolumeNameGatewayData = "ag-lib-gateway-data"
ActiveGateVolumeNameLog = "ag-log-gateway"
ActiveGateVolumeNameTmp = "ag-tmp-gateway"

ActiveGateDirectoryGatewayConfig = "/var/lib/dynatrace/gateway/config"
ActiveGateDirectoryGatewayTemp = "/var/lib/dynatrace/gateway/temp"
ActiveGateDirectoryGatewayData = "/var/lib/dynatrace/gateway/data"
ActiveGateDirectoryLog = "/var/log/dynatrace/gateway"
ActiveGateDirectoryTmp = "/var/tmp/dynatrace/gateway"

HttpsServicePortName = "https"
HttpsServicePort = 443
HttpServicePortName = "http"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const extensionsRuntimeDir = "/var/lib/dynatrace/remotepluginmodule/agent/conf/r
const activeGateInternalCommunicationPort = 9999

const (
eecAuthToken = "auth-tokens"

dataSourceStartupArguments = "eec-ds-shared"
dataSourceAuthToken = "dsauthtokendir"
eecLogs = "extensions-logs"
Expand Down Expand Up @@ -74,12 +72,6 @@ func (eec *ExtensionController) BuildContainer() corev1.Container {

func (eec *ExtensionController) BuildVolumes() []corev1.Volume {
volumes := []corev1.Volume{
{
Name: eecAuthToken,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: dataSourceStartupArguments,
VolumeSource: corev1.VolumeSource{
Expand Down Expand Up @@ -148,7 +140,7 @@ func (eec *ExtensionController) buildCommand() []string {

func (eec *ExtensionController) buildVolumeMounts() []corev1.VolumeMount {
return []corev1.VolumeMount{
{Name: eecAuthToken, MountPath: activeGateConfigDir},
{Name: capability.ActiveGateVolumeNameGatewayConfig, ReadOnly: true, MountPath: capability.ActiveGateDirectoryGatewayConfig},
{Name: dataSourceStartupArguments, MountPath: dataSourceStartupArgsMountPoint},
{Name: dataSourceAuthToken, MountPath: dataSourceAuthTokenMountPoint},
{Name: dataSourceMetadata, MountPath: statsdMetadataMountPoint, ReadOnly: true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package statefulset
import (
"testing"

"github.com/Dynatrace/dynatrace-operator/src/controllers/activegate/capability"
"github.com/Dynatrace/dynatrace-operator/src/kubeobjects"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -37,7 +38,7 @@ func TestExtensionController_BuildContainerAndVolumes(t *testing.T) {
}

for _, mountPath := range []string{
activeGateConfigDir,
capability.ActiveGateDirectoryGatewayConfig,
dataSourceStartupArgsMountPoint,
dataSourceAuthTokenMountPoint,
statsdMetadataMountPoint,
Expand All @@ -47,6 +48,10 @@ func TestExtensionController_BuildContainerAndVolumes(t *testing.T) {
assertion.Truef(kubeobjects.MountPathIsIn(container.VolumeMounts, mountPath), "Expected that EEC container defines mount point %s", mountPath)
}

assert.Truef(t, kubeobjects.MountPathIsReadOnly(container.VolumeMounts, capability.ActiveGateDirectoryGatewayConfig),
"Expected that ActiveGate container mount point %s is mounted ReadOnly", capability.ActiveGateDirectoryGatewayConfig,
)

for _, envVar := range []string{
envTenantId, envServerUrl, envEecIngestPort,
} {
Expand Down
99 changes: 93 additions & 6 deletions src/controllers/activegate/reconciler/statefulset/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const (
serviceAccountPrefix = "dynatrace-"
tenantSecretVolumeName = "ag-tenant-secret"

annotationVersion = dynatracev1beta1.InternalFlagPrefix + "version"
annotationCustomPropsHash = dynatracev1beta1.InternalFlagPrefix + "custom-properties-hash"
annotationVersion = dynatracev1beta1.InternalFlagPrefix + "version"
annotationCustomPropsHash = dynatracev1beta1.InternalFlagPrefix + "custom-properties-hash"
annotationActiveGateContainerAppArmor = "container.apparmor.security.beta.kubernetes.io/" + capability.ActiveGateContainerName

dtServer = "DT_SERVER"
dtTenant = "DT_TENANT"
Expand All @@ -35,7 +36,6 @@ const (
dtGroup = "DT_GROUP"
dtDeploymentMetadata = "DT_DEPLOYMENT_METADATA"

activeGateConfigDir = "/var/lib/dynatrace/gateway/config"
dataSourceStartupArgsMountPoint = "/mnt/dsexecargs"
dataSourceAuthTokenMountPoint = "/var/lib/dynatrace/remotepluginmodule/agent/runtime/datasources"
dataSourceMetadataMountPoint = "/mnt/dsmetadata"
Expand Down Expand Up @@ -96,8 +96,9 @@ func CreateStatefulSet(stsProperties *statefulSetProperties) (*appsv1.StatefulSe
ObjectMeta: metav1.ObjectMeta{
Labels: buildLabels(stsProperties.DynaKube, stsProperties.feature, stsProperties.CapabilityProperties),
Annotations: map[string]string{
annotationVersion: stsProperties.Status.ActiveGate.Version,
annotationCustomPropsHash: stsProperties.customPropertiesHash,
annotationVersion: stsProperties.Status.ActiveGate.Version,
annotationCustomPropsHash: stsProperties.customPropertiesHash,
annotationActiveGateContainerAppArmor: "runtime/default",
},
},
Spec: buildTemplateSpec(stsProperties),
Expand Down Expand Up @@ -178,6 +179,10 @@ func buildContainers(stsProperties *statefulSetProperties, extraContainerBuilder
return containers
}

func pointerToBool(value bool) *bool {
return &value
}

func buildActiveGateContainer(stsProperties *statefulSetProperties) corev1.Container {
return corev1.Container{
Name: capability.ActiveGateContainerName,
Expand All @@ -198,6 +203,20 @@ func buildActiveGateContainer(stsProperties *statefulSetProperties) corev1.Conta
PeriodSeconds: 15,
FailureThreshold: 3,
},
SecurityContext: &corev1.SecurityContext{
Privileged: pointerToBool(false),
AllowPrivilegeEscalation: pointerToBool(false),
ReadOnlyRootFilesystem: pointerToBool(true),
RunAsNonRoot: pointerToBool(true),
Capabilities: &corev1.Capabilities{
Drop: []corev1.Capability{
"all",
},
},
SeccompProfile: &corev1.SeccompProfile{
Type: corev1.SeccompProfileTypeRuntimeDefault,
},
},
}
}

Expand Down Expand Up @@ -240,9 +259,46 @@ func buildVolumes(stsProperties *statefulSetProperties, extraContainerBuilders [
volumes = append(volumes, buildProxyVolumes()...)
}

volumes = append(volumes, buildActiveGateVolumes()...)

return volumes
}

func buildActiveGateVolumes() []corev1.Volume {
return []corev1.Volume{
{
Name: capability.ActiveGateVolumeNameGatewayConfig,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: capability.ActiveGateVolumeNameGatewayTemp,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: capability.ActiveGateVolumeNameGatewayData,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: capability.ActiveGateVolumeNameLog,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
{
Name: capability.ActiveGateVolumeNameTmp,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
}
}

func buildProxyVolumes() []corev1.Volume {
return []corev1.Volume{
{
Expand Down Expand Up @@ -277,7 +333,6 @@ func buildVolumeMounts(stsProperties *statefulSetProperties) []corev1.VolumeMoun

if stsProperties.NeedsStatsd() {
volumeMounts = append(volumeMounts,
corev1.VolumeMount{Name: eecAuthToken, MountPath: activeGateConfigDir},
corev1.VolumeMount{Name: eecLogs, MountPath: extensionsLogsDir + "/eec", ReadOnly: true},
corev1.VolumeMount{Name: dataSourceStatsdLogs, MountPath: extensionsLogsDir + "/statsd", ReadOnly: true},
)
Expand All @@ -297,9 +352,41 @@ func buildVolumeMounts(stsProperties *statefulSetProperties) []corev1.VolumeMoun
},
)

volumeMounts = append(volumeMounts, buildActiveGateVolumeMounts()...)

return volumeMounts
}

func buildActiveGateVolumeMounts() []corev1.VolumeMount {
return []corev1.VolumeMount{
{
ReadOnly: false,
Name: capability.ActiveGateVolumeNameGatewayConfig,
MountPath: capability.ActiveGateDirectoryGatewayConfig,
},
{
ReadOnly: false,
Name: capability.ActiveGateVolumeNameGatewayTemp,
MountPath: capability.ActiveGateDirectoryGatewayTemp,
},
{
ReadOnly: false,
Name: capability.ActiveGateVolumeNameGatewayData,
MountPath: capability.ActiveGateDirectoryGatewayData,
},
{
ReadOnly: false,
Name: capability.ActiveGateVolumeNameLog,
MountPath: capability.ActiveGateDirectoryLog,
},
{
ReadOnly: false,
Name: capability.ActiveGateVolumeNameTmp,
MountPath: capability.ActiveGateDirectoryTmp,
},
}
}

func buildProxyMounts() []corev1.VolumeMount {
return []corev1.VolumeMount{
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ func TestStatefulSetBuilder_Build(t *testing.T) {
t.Run(`template has annotations`, func(t *testing.T) {
sts, _ := CreateStatefulSet(NewStatefulSetProperties(instance, capabilityProperties, "", testValue, "", "", "", nil, nil, nil))
assert.Equal(t, map[string]string{
annotationVersion: instance.Status.ActiveGate.Version,
annotationCustomPropsHash: testValue,
annotationVersion: instance.Status.ActiveGate.Version,
annotationCustomPropsHash: testValue,
annotationActiveGateContainerAppArmor: "runtime/default",
}, sts.Spec.Template.Annotations)
})
}
Expand All @@ -101,9 +102,6 @@ func TestStatefulSet_TemplateSpec(t *testing.T) {
}

checkVolumeMounts := func(expected bool, templateSpec *corev1.PodSpec) {
assert.Equalf(t, expected, kubeobjects.VolumeIsDefined(templateSpec.Volumes, "auth-tokens"),
"Expected that volume mount %s has a predefined pod volume", "auth-tokens",
)
assert.Equalf(t, expected, kubeobjects.VolumeIsDefined(templateSpec.Volumes, dataSourceMetadata),
"Expected that volume mount %s has a predefined pod volume", dataSourceMetadata,
)
Expand Down Expand Up @@ -162,9 +160,28 @@ func TestStatefulSet_Container(t *testing.T) {
assert.NotEmpty(t, activeGateContainer.Env)
assert.Empty(t, activeGateContainer.Args)

assert.Equalf(t, instance.NeedsStatsd(), kubeobjects.MountPathIsIn(activeGateContainer.VolumeMounts, activeGateConfigDir),
"Expected that ActiveGate container defines mount point %s if and only if StatsD ingest is enabled", activeGateConfigDir,
)
assert.Equal(t, *activeGateContainer.SecurityContext.Privileged, false)
assert.Equal(t, *activeGateContainer.SecurityContext.AllowPrivilegeEscalation, false)
assert.Equal(t, *activeGateContainer.SecurityContext.ReadOnlyRootFilesystem, true)
assert.Equal(t, *activeGateContainer.SecurityContext.RunAsNonRoot, true)
assert.Equal(t, activeGateContainer.SecurityContext.SeccompProfile.Type, corev1.SeccompProfileTypeRuntimeDefault)
assert.Equal(t, len(activeGateContainer.SecurityContext.Capabilities.Drop), 1)
assert.Equal(t, activeGateContainer.SecurityContext.Capabilities.Drop[0], corev1.Capability("all"))

for _, directory := range []string{
capability.ActiveGateDirectoryGatewayConfig,
capability.ActiveGateDirectoryGatewayTemp,
capability.ActiveGateDirectoryGatewayData,
capability.ActiveGateDirectoryLog,
capability.ActiveGateDirectoryTmp,
} {
assert.Truef(t, kubeobjects.MountPathIsIn(activeGateContainer.VolumeMounts, directory),
"Expected that ActiveGate container defines mount point %s", directory,
)
assert.Truef(t, kubeobjects.MountPathIsReadWrite(activeGateContainer.VolumeMounts, directory),
"Expected that ActiveGate container mount point %s is mounted ReadWrite", directory,
)
}
assert.Equalf(t, instance.NeedsStatsd(), kubeobjects.MountPathIsIn(activeGateContainer.VolumeMounts, extensionsLogsDir+"/eec"),
"Expected that ActiveGate container defines mount point %s if and only if StatsD ingest is enabled", extensionsLogsDir+"/eec",
)
Expand Down Expand Up @@ -199,7 +216,7 @@ func TestStatefulSet_Volumes(t *testing.T) {
volumes := buildVolumes(stsProperties, getContainerBuilders(stsProperties))
expectedSecretName := instance.Name + "-router-" + customproperties.Suffix

require.Equal(t, 2, len(volumes))
require.Equal(t, 7, len(volumes))

customPropertiesVolume, err := kubeobjects.GetVolumeByName(volumes, customproperties.VolumeName)
assert.NoError(t, err)
Expand All @@ -221,7 +238,7 @@ func TestStatefulSet_Volumes(t *testing.T) {
volumes := buildVolumes(stsProperties, getContainerBuilders(stsProperties))
expectedSecretName := testKey

require.Equal(t, 2, len(volumes))
require.Equal(t, 7, len(volumes))

customPropertiesVolume, err := kubeobjects.GetVolumeByName(volumes, customproperties.VolumeName)
assert.NoError(t, err)
Expand Down
18 changes: 18 additions & 0 deletions src/kubeobjects/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ func MountPathIsIn(volumeMounts []corev1.VolumeMount, mountPathToCheck string) b
return false
}

func MountPathIsReadOnly(volumeMounts []corev1.VolumeMount, mountPathToCheck string) bool {
for _, volMount := range volumeMounts {
if volMount.MountPath == mountPathToCheck && volMount.ReadOnly {
return true
}
}
return false
}

func MountPathIsReadWrite(volumeMounts []corev1.VolumeMount, mountPathToCheck string) bool {
for _, volMount := range volumeMounts {
if volMount.MountPath == mountPathToCheck && !volMount.ReadOnly {
return true
}
}
return false
}

func VolumeIsDefined(volumes []corev1.Volume, volumeNameToCheck string) bool {
for _, vol := range volumes {
if vol.Name == volumeNameToCheck {
Expand Down

0 comments on commit 48b51a5

Please sign in to comment.