Skip to content

Commit e0c8b06

Browse files
lmicciniclaude
andcommitted
Harden Redis and InstanceHA workload security
Reduce Redis workload SA pod verbs from create/get/list/watch/update/patch/delete to get/list/watch/patch — the only verb actually used is PATCH for pod label operations in common.sh. Add SecurityContext to Redis containers (RunAsUser/ RunAsGroup 42460), switch InstanceHA SCC from anyuid to nonroot-v2, add SeccompProfile RuntimeDefault to InstanceHA container. Disable AutomountServiceAccountToken on both workloads and replace with a projected volume providing a time-limited (1h) SA token at the standard mount path. Depends-On: openstack-k8s-operators/lib-common#720 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b5154b6 commit e0c8b06

12 files changed

Lines changed: 223 additions & 21 deletions

File tree

apis/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ require (
7070

7171
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4 //allow-merging
7272

73+
replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43 //allow-merging
74+
7375
// pin these to avoid later versions pulled
7476
replace k8s.io/apimachinery => k8s.io/apimachinery v0.33.13 //allow-merging
7577

apis/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
6464
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
6565
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
6666
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
67+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43 h1:8u7ajj01pWiPKBmeVGP81Q9WCxGX14otNkqq/AvHG38=
68+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43/go.mod h1:yA4tbcB62Dml5d0SP3n14hbwLVrKFicm690/eTWvusc=
6769
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
6870
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
6971
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -81,8 +83,6 @@ github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
8183
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
8284
github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4 h1:YVY7Mb0AdJBbNgC/Zz9R/ZwhixOmZNA8P+6ZPaDW/0M=
8385
github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4/go.mod h1:SPLf21TYPipzCO67BURkCfK6dcIIxx0oNRVWaOyRcXM=
84-
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260717092345-ab1ee7b97c67 h1:crgFl4DA5n38huc043ux21jEQM7UaRMXsi+wogDOYn0=
85-
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260717092345-ab1ee7b97c67/go.mod h1:yA4tbcB62Dml5d0SP3n14hbwLVrKFicm690/eTWvusc=
8686
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
8787
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
8888
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

config/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ rules:
350350
- securitycontextconstraints
351351
verbs:
352352
- use
353+
- apiGroups:
354+
- security.openshift.io
355+
resourceNames:
356+
- nonroot-v2
357+
resources:
358+
- securitycontextconstraints
359+
verbs:
360+
- use
353361
- apiGroups:
354362
- topology.openstack.org
355363
resources:

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ require (
109109

110110
replace github.com/openstack-k8s-operators/infra-operator/apis => ./apis
111111

112+
replace github.com/openstack-k8s-operators/lib-common/modules/common => github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43 //allow-merging
113+
112114
// mschuppert: map to latest commit from release-4.20 tag
113115
// must consistent within modules and service operators
114116
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4 //allow-merging

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
9494
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
9595
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
9696
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
97+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43 h1:8u7ajj01pWiPKBmeVGP81Q9WCxGX14otNkqq/AvHG38=
98+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260722124611-734fbfdeba43/go.mod h1:yA4tbcB62Dml5d0SP3n14hbwLVrKFicm690/eTWvusc=
9799
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
98100
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
99101
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
@@ -116,8 +118,6 @@ github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
116118
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
117119
github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4 h1:YVY7Mb0AdJBbNgC/Zz9R/ZwhixOmZNA8P+6ZPaDW/0M=
118120
github.com/openshift/api v0.0.0-20260710141509-36dec0bfafe4/go.mod h1:SPLf21TYPipzCO67BURkCfK6dcIIxx0oNRVWaOyRcXM=
119-
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260717092345-ab1ee7b97c67 h1:crgFl4DA5n38huc043ux21jEQM7UaRMXsi+wogDOYn0=
120-
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260717092345-ab1ee7b97c67/go.mod h1:yA4tbcB62Dml5d0SP3n14hbwLVrKFicm690/eTWvusc=
121121
github.com/openstack-k8s-operators/lib-common/modules/edpm v0.0.0-20260717092345-ab1ee7b97c67 h1:EoBTLbbfGRC37uA0J6al5zeZfQSxy6XwsUMNZAWCQPA=
122122
github.com/openstack-k8s-operators/lib-common/modules/edpm v0.0.0-20260717092345-ab1ee7b97c67/go.mod h1:IN0pCq/v2MsPcXrtcBIqcpVEqyq8oTIbNahmsc3tu28=
123123
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260717092345-ab1ee7b97c67 h1:GmakXxpm0scnNPuFDC9fSdhqo8MPyyBqqL0UW8pNh5o=

internal/controller/instanceha/instanceha_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (r *Reconciler) GetLogger(ctx context.Context) logr.Logger {
9595
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=roles,verbs=get;list;watch;create;update;patch
9696
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebindings,verbs=get;list;watch;create;update;patch
9797
// service account permissions that are needed to grant permission to the above
98-
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=anyuid,resources=securitycontextconstraints,verbs=use
98+
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=nonroot-v2,resources=securitycontextconstraints,verbs=use
9999
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
100100
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
101101
// +kubebuilder:rbac:groups=topology.openstack.org,resources=topologies,verbs=get;list;watch;update
@@ -205,7 +205,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
205205
rbacRules := []rbacv1.PolicyRule{
206206
{
207207
APIGroups: []string{"security.openshift.io"},
208-
ResourceNames: []string{"anyuid"},
208+
ResourceNames: []string{"nonroot-v2"},
209209
Resources: []string{"securitycontextconstraints"},
210210
Verbs: []string{"use"},
211211
},

internal/controller/redis/redis_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ type Reconciler struct {
107107
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebindings,verbs=get;list;watch;create;update
108108
// service account permissions that are needed to grant permission to the above
109109
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=anyuid,resources=securitycontextconstraints,verbs=use
110-
// +kubebuilder:rbac:groups="",resources=pods,verbs=create;delete;get;list;patch;update;watch
110+
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;patch;watch
111111
// +kubebuilder:rbac:groups=topology.openstack.org,resources=topologies,verbs=get;list;watch;update
112112

113113
// Reconcile - Redis
@@ -219,7 +219,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
219219
{
220220
APIGroups: []string{""},
221221
Resources: []string{"pods"},
222-
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
222+
Verbs: []string{"get", "list", "watch", "patch"},
223223
},
224224
}
225225
rbacResult, err := common_rbac.ReconcileRbac(ctx, helper, instance, rbacRules)

internal/instanceha/funcs.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ limitations under the License.
1414
package instanceha
1515

1616
import (
17+
"fmt"
18+
1719
instancehav1 "github.com/openstack-k8s-operators/infra-operator/apis/instanceha/v1beta1"
1820
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
1921
env "github.com/openstack-k8s-operators/lib-common/modules/common/env"
22+
"github.com/openstack-k8s-operators/lib-common/modules/common/serviceaccount"
2023
"github.com/openstack-k8s-operators/lib-common/modules/common/tls"
21-
22-
"fmt"
2324
appsv1 "k8s.io/api/apps/v1"
2425
corev1 "k8s.io/api/core/v1"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26-
2727
"k8s.io/apimachinery/pkg/util/intstr"
2828
"k8s.io/utils/ptr"
2929
)
@@ -172,11 +172,12 @@ func Deployment(
172172
Annotations: annotations,
173173
},
174174
Spec: corev1.PodSpec{
175-
ServiceAccountName: instance.RbacResourceName(),
175+
ServiceAccountName: instance.RbacResourceName(),
176+
AutomountServiceAccountToken: ptr.To(false),
176177
SecurityContext: &corev1.PodSecurityContext{
177178
FSGroup: ptr.To(instanceHaUID),
178179
},
179-
Volumes: volumes,
180+
Volumes: append(volumes, serviceaccount.KubeAPIAccessVolume()),
180181
TerminationGracePeriodSeconds: ptr.To[int64](45),
181182
Containers: []corev1.Container{{
182183
Name: "instanceha",
@@ -193,10 +194,13 @@ func Deployment(
193194
"ALL",
194195
},
195196
},
197+
SeccompProfile: &corev1.SeccompProfile{
198+
Type: corev1.SeccompProfileTypeRuntimeDefault,
199+
},
196200
},
197201
Env: env.MergeEnvs([]corev1.EnvVar{}, envVars),
198202
Ports: instancehaPorts(instance),
199-
VolumeMounts: volumeMounts,
203+
VolumeMounts: append(volumeMounts, serviceaccount.KubeAPIAccessVolumeMount()),
200204
LivenessProbe: livenessProbe,
201205
ReadinessProbe: readinessProbe,
202206
}},

internal/instanceha/funcs_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
. "github.com/onsi/gomega" //revive:disable:dot-imports
77

88
instancehav1 "github.com/openstack-k8s-operators/infra-operator/apis/instanceha/v1beta1"
9+
corev1 "k8s.io/api/core/v1"
910
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1011
)
1112

@@ -101,6 +102,51 @@ func TestDeploymentSecurityContext(t *testing.T) {
101102
g.Expect(container.SecurityContext.ReadOnlyRootFilesystem).NotTo(BeNil())
102103
g.Expect(*container.SecurityContext.ReadOnlyRootFilesystem).To(BeTrue())
103104

105+
g.Expect(container.SecurityContext.RunAsNonRoot).NotTo(BeNil())
106+
g.Expect(*container.SecurityContext.RunAsNonRoot).To(BeTrue())
107+
108+
g.Expect(container.SecurityContext.RunAsUser).NotTo(BeNil())
109+
g.Expect(*container.SecurityContext.RunAsUser).To(Equal(int64(42401)))
110+
111+
g.Expect(container.SecurityContext.RunAsGroup).NotTo(BeNil())
112+
g.Expect(*container.SecurityContext.RunAsGroup).To(Equal(int64(42401)))
113+
114+
g.Expect(container.SecurityContext.AllowPrivilegeEscalation).NotTo(BeNil())
115+
g.Expect(*container.SecurityContext.AllowPrivilegeEscalation).To(BeFalse())
116+
117+
g.Expect(container.SecurityContext.Capabilities).NotTo(BeNil())
118+
g.Expect(container.SecurityContext.Capabilities.Drop).To(ContainElement(corev1.Capability("ALL")))
119+
120+
g.Expect(container.SecurityContext.SeccompProfile).NotTo(BeNil())
121+
g.Expect(container.SecurityContext.SeccompProfile.Type).To(Equal(corev1.SeccompProfileTypeRuntimeDefault))
122+
123+
// Verify AutomountServiceAccountToken is disabled
124+
g.Expect(dep.Spec.Template.Spec.AutomountServiceAccountToken).NotTo(BeNil())
125+
g.Expect(*dep.Spec.Template.Spec.AutomountServiceAccountToken).To(BeFalse())
126+
127+
// Verify kube-api-access projected volume exists
128+
var kubeAPIVolumeFound bool
129+
for _, vol := range dep.Spec.Template.Spec.Volumes {
130+
if vol.Name == "kube-api-access" {
131+
kubeAPIVolumeFound = true
132+
g.Expect(vol.VolumeSource.Projected).NotTo(BeNil())
133+
g.Expect(vol.VolumeSource.Projected.Sources).To(HaveLen(3))
134+
break
135+
}
136+
}
137+
g.Expect(kubeAPIVolumeFound).To(BeTrue(), "kube-api-access projected volume should exist")
138+
139+
// Verify kube-api-access mount exists
140+
var kubeAPIMountFound bool
141+
for _, mount := range container.VolumeMounts {
142+
if mount.Name == "kube-api-access" && mount.MountPath == "/var/run/secrets/kubernetes.io/serviceaccount" {
143+
kubeAPIMountFound = true
144+
g.Expect(mount.ReadOnly).To(BeTrue())
145+
break
146+
}
147+
}
148+
g.Expect(kubeAPIMountFound).To(BeTrue(), "kube-api-access volume mount should exist")
149+
104150
// Verify /tmp emptyDir volume exists
105151
var tmpVolumeFound bool
106152
for _, vol := range dep.Spec.Template.Spec.Volumes {

internal/redis/const.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package redis
17+
18+
const (
19+
// RedisUID -
20+
// https://github.com/openstack/kolla/blob/master/kolla/common/users.py
21+
RedisUID int64 = 42460
22+
)

0 commit comments

Comments
 (0)