From cf1df89ddc26180c5e99b155b2e04abe32b06e0c Mon Sep 17 00:00:00 2001 From: Lukas Piwowarski Date: Mon, 12 Oct 2026 15:22:46 +0200 Subject: [PATCH] fix: Goose pod security context When .spec.storage is configured, Goose uses the mounted volume as its working directory. A root-owned PVC prevents the Goose process, which runs as UID/GID 1000, from writing its home-directory data under /home/goose, including configuration and transcripts: mkdir: cannot create directory '/home/goose/.config': Permission denied Apply the standard restrictive pod and container security contexts with Goose UID/GID 1000, matching openstackclient. Set fsGroup to 1000 so supported PVC-backed working directories are group-writable and Goose can persist its home-directory data. Depends-On: https://github.com/openstack-k8s-operators/lib-common/pull/780 --- internal/openstackassistant/funcs.go | 23 +++++++++-------------- internal/openstackassistant/funcs_test.go | 11 +++++++++++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/internal/openstackassistant/funcs.go b/internal/openstackassistant/funcs.go index 2369b7840e..8bbeb9d31d 100644 --- a/internal/openstackassistant/funcs.go +++ b/internal/openstackassistant/funcs.go @@ -20,7 +20,9 @@ import ( "unicode" env "github.com/openstack-k8s-operators/lib-common/modules/common/env" + "github.com/openstack-k8s-operators/lib-common/modules/common/pod" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + "github.com/openstack-k8s-operators/lib-common/modules/users" assistantv1 "github.com/openstack-k8s-operators/openstack-operator/api/assistant/v1beta1" corev1 "k8s.io/api/core/v1" @@ -143,23 +145,16 @@ func AssistantPodSpecWithCABundle( podSpec := corev1.PodSpec{ TerminationGracePeriodSeconds: ptr.To[int64](0), ServiceAccountName: instance.RbacResourceName(), + SecurityContext: pod.RestrictivePodSecurityContext(users.OpenStackAssistantUID, users.OpenStackAssistantGID), Volumes: volumes, Containers: []corev1.Container{ { - Name: "assistant", - Image: instance.Spec.ContainerImage, - SecurityContext: &corev1.SecurityContext{ - RunAsNonRoot: ptr.To(true), - AllowPrivilegeEscalation: ptr.To(false), - Capabilities: &corev1.Capabilities{ - Drop: []corev1.Capability{ - "ALL", - }, - }, - }, - Env: containerEnvs, - VolumeMounts: volumeMounts, - Resources: instance.Spec.Resources, + Name: "assistant", + Image: instance.Spec.ContainerImage, + SecurityContext: pod.RestrictiveSecurityContext(users.OpenStackAssistantUID, users.OpenStackAssistantGID), + Env: containerEnvs, + VolumeMounts: volumeMounts, + Resources: instance.Spec.Resources, }, }, } diff --git a/internal/openstackassistant/funcs_test.go b/internal/openstackassistant/funcs_test.go index 5be79396f0..d650d71ac7 100644 --- a/internal/openstackassistant/funcs_test.go +++ b/internal/openstackassistant/funcs_test.go @@ -17,10 +17,12 @@ import ( "github.com/onsi/gomega" "github.com/openstack-k8s-operators/lib-common/modules/common/tls" + "github.com/openstack-k8s-operators/lib-common/modules/users" assistantv1 "github.com/openstack-k8s-operators/openstack-operator/api/assistant/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" ) func newTestInstance() *assistantv1.OpenStackAssistant { @@ -111,8 +113,17 @@ func TestAssistantPodSpec_SecurityContext(t *testing.T) { instance := newTestInstance() spec := AssistantPodSpec(instance, "hash", nil) + podSC := spec.SecurityContext sc := spec.Containers[0].SecurityContext + g.Expect(podSC.RunAsUser).To(gomega.Equal(ptr.To(users.OpenStackAssistantUID))) + g.Expect(podSC.RunAsGroup).To(gomega.Equal(ptr.To(users.OpenStackAssistantGID))) + g.Expect(podSC.FSGroup).To(gomega.Equal(ptr.To(users.OpenStackAssistantGID))) + g.Expect(podSC.SeccompProfile).To(gomega.Equal(&corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + })) + g.Expect(sc.RunAsUser).To(gomega.Equal(ptr.To(users.OpenStackAssistantUID))) + g.Expect(sc.RunAsGroup).To(gomega.Equal(ptr.To(users.OpenStackAssistantGID))) g.Expect(*sc.RunAsNonRoot).To(gomega.BeTrue()) g.Expect(*sc.AllowPrivilegeEscalation).To(gomega.BeFalse()) g.Expect(sc.Capabilities.Drop).To(gomega.ContainElement(corev1.Capability("ALL")))