diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 6cfa4354f7..a48dceaa41 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -68,6 +68,7 @@ const ( sidecarCPULimitFlag = "sidecar-cpu-limit" sidecarMemoryRequestFlag = "sidecar-memory-request" sidecarMemoryLimitFlag = "sidecar-memory-limit" + sidecarRunAsUserFlag = "sidecar-run-as-user" sdkServerAccountFlag = "sdk-service-account" pullSidecarFlag = "always-pull-sidecar" minPortFlag = "min-port" @@ -214,7 +215,7 @@ func main() { gsController := gameservers.NewController(controllerHooks, health, ctlConf.PortRanges, ctlConf.SidecarImage, ctlConf.AlwaysPullSidecar, ctlConf.SidecarCPURequest, ctlConf.SidecarCPULimit, - ctlConf.SidecarMemoryRequest, ctlConf.SidecarMemoryLimit, ctlConf.SdkServiceAccount, + ctlConf.SidecarMemoryRequest, ctlConf.SidecarMemoryLimit, ctlConf.SidecarRunAsUser, ctlConf.SdkServiceAccount, kubeClient, kubeInformerFactory, extClient, agonesClient, agonesInformerFactory) gsSetController := gameserversets.NewController(health, gsCounter, kubeClient, extClient, agonesClient, agonesInformerFactory) @@ -260,6 +261,7 @@ func parseEnvFlags() config { viper.SetDefault(sidecarCPULimitFlag, "0") viper.SetDefault(sidecarMemoryRequestFlag, "0") viper.SetDefault(sidecarMemoryLimitFlag, "0") + viper.SetDefault(sidecarRunAsUserFlag, "1000") viper.SetDefault(pullSidecarFlag, false) viper.SetDefault(sdkServerAccountFlag, "agones-sdk") viper.SetDefault(certFileFlag, filepath.Join(base, "certs", "server.crt")) @@ -284,6 +286,7 @@ func parseEnvFlags() config { pflag.String(sidecarCPURequestFlag, viper.GetString(sidecarCPURequestFlag), "Flag to overwrite the GameServer sidecar container's cpu request. Can also use SIDECAR_CPU_REQUEST env variable") pflag.String(sidecarMemoryLimitFlag, viper.GetString(sidecarMemoryLimitFlag), "Flag to overwrite the GameServer sidecar container's memory limit. Can also use SIDECAR_MEMORY_LIMIT env variable") pflag.String(sidecarMemoryRequestFlag, viper.GetString(sidecarMemoryRequestFlag), "Flag to overwrite the GameServer sidecar container's memory request. Can also use SIDECAR_MEMORY_REQUEST env variable") + pflag.Int32(sidecarRunAsUserFlag, viper.GetInt32(sidecarRunAsUserFlag), "Flag to indicate the GameServer sidecar container's UID. Can also use SIDECAR_RUN_AS_USER env variable") pflag.Bool(pullSidecarFlag, viper.GetBool(pullSidecarFlag), "For development purposes, set the sidecar image to have a ImagePullPolicy of Always. Can also use ALWAYS_PULL_SIDECAR env variable") pflag.String(sdkServerAccountFlag, viper.GetString(sdkServerAccountFlag), "Overwrite what service account default for GameServer Pods. Defaults to Can also use SDK_SERVICE_ACCOUNT") pflag.Int32(minPortFlag, 0, "Required. The minimum port that that a GameServer can be allocated to. Can also use MIN_PORT env variable.") @@ -315,6 +318,7 @@ func parseEnvFlags() config { runtime.Must(viper.BindEnv(sidecarCPURequestFlag)) runtime.Must(viper.BindEnv(sidecarMemoryLimitFlag)) runtime.Must(viper.BindEnv(sidecarMemoryRequestFlag)) + runtime.Must(viper.BindEnv(sidecarRunAsUserFlag)) runtime.Must(viper.BindEnv(pullSidecarFlag)) runtime.Must(viper.BindEnv(sdkServerAccountFlag)) runtime.Must(viper.BindEnv(minPortFlag)) @@ -378,6 +382,7 @@ func parseEnvFlags() config { SidecarCPULimit: limitCPU, SidecarMemoryRequest: requestMemory, SidecarMemoryLimit: limitMemory, + SidecarRunAsUser: int(viper.GetInt32(sidecarRunAsUserFlag)), SdkServiceAccount: viper.GetString(sdkServerAccountFlag), AlwaysPullSidecar: viper.GetBool(pullSidecarFlag), KeyFile: viper.GetString(keyFileFlag), @@ -430,6 +435,7 @@ type config struct { SidecarCPULimit resource.Quantity SidecarMemoryRequest resource.Quantity SidecarMemoryLimit resource.Quantity + SidecarRunAsUser int SdkServiceAccount string AlwaysPullSidecar bool PrometheusMetrics bool diff --git a/install/helm/agones/templates/controller.yaml b/install/helm/agones/templates/controller.yaml index a5a7045537..429401d5e2 100644 --- a/install/helm/agones/templates/controller.yaml +++ b/install/helm/agones/templates/controller.yaml @@ -134,6 +134,8 @@ spec: value: {{ .Values.agones.image.sdk.memoryRequest | quote }} - name: SIDECAR_MEMORY_LIMIT value: {{ .Values.agones.image.sdk.memoryLimit | quote }} + - name: SIDECAR_RUN_AS_USER + value: "1000" - name: SDK_SERVICE_ACCOUNT value: {{ .Values.agones.serviceaccount.sdk.name | quote }} - name: PROMETHEUS_EXPORTER diff --git a/install/yaml/install.yaml b/install/yaml/install.yaml index b782919a3f..3a92b02f92 100644 --- a/install/yaml/install.yaml +++ b/install/yaml/install.yaml @@ -17084,6 +17084,8 @@ spec: value: "0" - name: SIDECAR_MEMORY_LIMIT value: "0" + - name: SIDECAR_RUN_AS_USER + value: "1000" - name: SDK_SERVICE_ACCOUNT value: "agones-sdk" - name: PROMETHEUS_EXPORTER diff --git a/pkg/gameservers/controller.go b/pkg/gameservers/controller.go index 3f69f4ed86..00d5aba426 100644 --- a/pkg/gameservers/controller.go +++ b/pkg/gameservers/controller.go @@ -56,6 +56,7 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" + "k8s.io/utils/pointer" ) const ( @@ -83,6 +84,7 @@ type Controller struct { sidecarCPULimit resource.Quantity sidecarMemoryRequest resource.Quantity sidecarMemoryLimit resource.Quantity + sidecarRunAsUser int sdkServiceAccount string crdGetter apiextclientv1.CustomResourceDefinitionInterface podGetter typedcorev1.PodsGetter @@ -114,6 +116,7 @@ func NewController( sidecarCPULimit resource.Quantity, sidecarMemoryRequest resource.Quantity, sidecarMemoryLimit resource.Quantity, + sidecarRunAsUser int, sdkServiceAccount string, kubeClient kubernetes.Interface, kubeInformerFactory informers.SharedInformerFactory, @@ -133,6 +136,7 @@ func NewController( sidecarCPURequest: sidecarCPURequest, sidecarMemoryLimit: sidecarMemoryLimit, sidecarMemoryRequest: sidecarMemoryRequest, + sidecarRunAsUser: sidecarRunAsUser, alwaysPullSidecarImage: alwaysPullSidecarImage, sdkServiceAccount: sdkServiceAccount, crdGetter: extClient.ApiextensionsV1().CustomResourceDefinitions(), @@ -763,6 +767,13 @@ func (c *Controller) sidecar(gs *agonesv1.GameServer) corev1.Container { if c.alwaysPullSidecarImage { sidecar.ImagePullPolicy = corev1.PullAlways } + + sidecar.SecurityContext = &corev1.SecurityContext{ + AllowPrivilegeEscalation: pointer.Bool(false), + RunAsNonRoot: pointer.Bool(true), + RunAsUser: pointer.Int64(int64(c.sidecarRunAsUser)), + } + return sidecar } diff --git a/pkg/gameservers/controller_test.go b/pkg/gameservers/controller_test.go index 95be69d7dd..88bd8221d1 100644 --- a/pkg/gameservers/controller_test.go +++ b/pkg/gameservers/controller_test.go @@ -51,9 +51,10 @@ import ( ) const ( - ipFixture = "12.12.12.12" - ipv6Fixture = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" - nodeFixtureName = "node1" + ipFixture = "12.12.12.12" + ipv6Fixture = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + nodeFixtureName = "node1" + sidecarRunAsUser = 1000 ) var GameServerKind = metav1.GroupVersionKind(agonesv1.SchemeGroupVersion.WithKind("GameServer")) @@ -1306,6 +1307,9 @@ func TestControllerCreateGameServerPod(t *testing.T) { assert.Equal(t, "FEATURE_GATES", sidecarContainer.Env[2].Name) assert.Equal(t, "LOG_LEVEL", sidecarContainer.Env[3].Name) assert.Equal(t, string(fixture.Spec.SdkServer.LogLevel), sidecarContainer.Env[3].Value) + assert.Equal(t, *sidecarContainer.SecurityContext.AllowPrivilegeEscalation, false) + assert.Equal(t, *sidecarContainer.SecurityContext.RunAsNonRoot, true) + assert.Equal(t, *sidecarContainer.SecurityContext.RunAsUser, int64(sidecarRunAsUser)) gsContainer := pod.Spec.Containers[1] assert.Equal(t, fixture.Spec.Ports[0].HostPort, gsContainer.Ports[0].HostPort) @@ -2257,7 +2261,7 @@ func newFakeController() (*Controller, agtesting.Mocks) { map[string]portallocator.PortRange{agonesv1.DefaultPortRange: {MinPort: 10, MaxPort: 20}}, "sidecar:dev", false, resource.MustParse("0.05"), resource.MustParse("0.1"), - resource.MustParse("50Mi"), resource.MustParse("100Mi"), "sdk-service-account", + resource.MustParse("50Mi"), resource.MustParse("100Mi"), sidecarRunAsUser, "sdk-service-account", m.KubeClient, m.KubeInformerFactory, m.ExtClient, m.AgonesClient, m.AgonesInformerFactory) c.recorder = m.FakeRecorder return c, m