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

Remove serviceaccount for game server container #640

Merged
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
31 changes: 31 additions & 0 deletions pkg/apis/stable/v1alpha1/gameserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,19 @@ func (gs *GameServer) FindGameServerContainer() (int, corev1.Container, error) {
return -1, corev1.Container{}, errors.Errorf("Could not find a container named %s", gs.Spec.Container)
}

// ApplyToPodGameServerContainer applies func(v1.Container) to the pod's gameserver container
func (gs *GameServer) ApplyToPodGameServerContainer(pod *corev1.Pod, f func(corev1.Container) corev1.Container) *corev1.Pod {
for i, c := range pod.Spec.Containers {
if c.Name == gs.Spec.Container {
c = f(c)
pod.Spec.Containers[i] = c
break
}
}

return pod
}

// Pod creates a new Pod from the PodTemplateSpec
// attached to the GameServer resource
func (gs *GameServer) Pod(sidecars ...corev1.Container) (*corev1.Pod, error) {
Expand All @@ -364,8 +377,12 @@ func (gs *GameServer) Pod(sidecars ...corev1.Container) (*corev1.Pod, error) {

gs.podObjectMeta(pod)

// if the service account is not set, then you are in the "opinionated"
// mode. If the user sets the service account, we assume they know what they are
// doing, and don't disable the gameserver container.
if pod.Spec.ServiceAccountName == "" {
pod.Spec.ServiceAccountName = SidecarServiceAccountName
gs.disableServiceAccount(pod)
}

i, gsContainer, err := gs.FindGameServerContainer()
Expand Down Expand Up @@ -457,6 +474,20 @@ func (gs *GameServer) podScheduling(pod *corev1.Pod) {
}
}

// disableServiceAccount disables the service account for the gameserver container
func (gs *GameServer) disableServiceAccount(pod *corev1.Pod) {
// gameservers don't get access to the k8s api.
emptyVol := corev1.Volume{Name: "empty", VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}}}
pod.Spec.Volumes = append(pod.Spec.Volumes, emptyVol)
mount := corev1.VolumeMount{MountPath: "/var/run/secrets/kubernetes.io/serviceaccount", Name: emptyVol.Name, ReadOnly: true}

gs.ApplyToPodGameServerContainer(pod, func(c corev1.Container) corev1.Container {
c.VolumeMounts = append(c.VolumeMounts, mount)

return c
})
}

// HasPortPolicy checks if there is a port with a given
// PortPolicy
func (gs *GameServer) HasPortPolicy(policy PortPolicy) bool {
Expand Down
56 changes: 55 additions & 1 deletion pkg/apis/stable/v1alpha1/gameserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,28 @@ func TestGameServerPodScheduling(t *testing.T) {
})
}

func TestGameServerDisableServiceAccount(t *testing.T) {
t.Parallel()

gs := &GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gameserver", UID: "1234"}, Spec: GameServerSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "container", Image: "container/image"}},
},
}}}

gs.ApplyDefaults()
pod, err := gs.Pod()
assert.NoError(t, err)
assert.Len(t, pod.Spec.Containers, 1)
assert.Empty(t, pod.Spec.Containers[0].VolumeMounts)

gs.disableServiceAccount(pod)
assert.Len(t, pod.Spec.Containers, 1)
assert.Len(t, pod.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, "/var/run/secrets/kubernetes.io/serviceaccount", pod.Spec.Containers[0].VolumeMounts[0].MountPath)
}

func TestGameServerCountPorts(t *testing.T) {
fixture := &GameServer{Spec: GameServerSpec{Ports: []GameServerPort{
{PortPolicy: Dynamic},
Expand All @@ -425,7 +447,7 @@ func TestGameServerPatch(t *testing.T) {

assert.Contains(t, string(patch), `{"op":"replace","path":"/spec/container","value":"bear"}`)
}
func TestGetDevAddress(t *testing.T) {
func TestGameServerGetDevAddress(t *testing.T) {
devGs := &GameServer{
ObjectMeta: metav1.ObjectMeta{
Name: "dev-game",
Expand All @@ -451,3 +473,35 @@ func TestGetDevAddress(t *testing.T) {
assert.False(t, isDev, "dev-game should NOT have a dev-address")
assert.Equal(t, "", devAddress, "dev-address IP address should be 127.1.1.1")
}

func TestGameServerApplyToPodGameServerContainer(t *testing.T) {
t.Parallel()

name := "mycontainer"
gs := &GameServer{
Spec: GameServerSpec{
Container: name,
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Name: name, Image: "foo/mycontainer"},
{Name: "notmycontainer", Image: "foo/notmycontainer"},
},
},
},
},
}

p1 := &corev1.Pod{Spec: *gs.Spec.Template.Spec.DeepCopy()}

p2 := gs.ApplyToPodGameServerContainer(p1, func(c corev1.Container) corev1.Container {
// easy thing to change and test for
c.TTY = true

return c
})

assert.Len(t, p2.Spec.Containers, 2)
assert.True(t, p2.Spec.Containers[0].TTY)
assert.False(t, p2.Spec.Containers[1].TTY)
}
38 changes: 19 additions & 19 deletions pkg/gameservers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,27 +595,27 @@ func (c *Controller) sidecar(gs *v1alpha1.GameServer) corev1.Container {

// addGameServerHealthCheck adds the http health check to the GameServer container
func (c *Controller) addGameServerHealthCheck(gs *v1alpha1.GameServer, pod *corev1.Pod) {
if !gs.Spec.Health.Disabled {
for i, c := range pod.Spec.Containers {
if c.Name == gs.Spec.Container {
if c.LivenessProbe == nil {
c.LivenessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/gshealthz",
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: gs.Spec.Health.InitialDelaySeconds,
PeriodSeconds: gs.Spec.Health.PeriodSeconds,
FailureThreshold: gs.Spec.Health.FailureThreshold,
}
pod.Spec.Containers[i] = c
}
break
if gs.Spec.Health.Disabled {
return
}

gs.ApplyToPodGameServerContainer(pod, func(c corev1.Container) corev1.Container {
if c.LivenessProbe == nil {
c.LivenessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/gshealthz",
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: gs.Spec.Health.InitialDelaySeconds,
PeriodSeconds: gs.Spec.Health.PeriodSeconds,
FailureThreshold: gs.Spec.Health.FailureThreshold,
}
}
}

return c
})
}

// syncGameServerStartingState looks for a pod that has been scheduled for this GameServer
Expand Down
46 changes: 46 additions & 0 deletions site/content/en/docs/Advanced/service-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "GameServer Pod Service Accounts"
linkTitle: "Service Accounts"
date: 2019-03-14T04:30:37Z
publishDate: 2019-04-01
description: >
RBAC permissions and service accounts for the `GameServer` Pod.
---

## Default Settings

By default, Agones sets up service accounts and sets them appropriately for the `Pods` that are created for `GameServers`.

Since Agones provides `GameServer` `Pods` with a sidecar container that needs access to Agones Custom Resource Definitions,
`Pods` are configured with a service account with extra RBAC permissions to ensure that it can read and modify the resources it needs.

Since service accounts apply to all containers in a `Pod`, Agones will automatically overwrite the mounted key for the
service account in the container that is running the dedicate game server in the backing `Pod`. This is done
since game server containers are exposed publicly, and generally dom't require the extra permissions to access aspects
of the Kubernetes API.

## Bringing your own Service Account

If needed, you can provide your own service account on the `Pod` specification in the `GameServer` configuration.

For example:

```yaml
apiVersion: "stable.agones.dev/v1alpha1"
kind: GameServer
metadata:
generateName: "simple-udp-"
spec:
ports:
- name: default
containerPort: 7654
template:
spec:
serviceAccountName: my-special-service-account # a custom service account
containers:
- name: simple-udp
image: gcr.io/agones-images/udp-server:0.7
```

If a service account is configured, the mounted key is not overwritten, as it assumed that you want to have full control
of the service account and underlying RBAC permissions.