Skip to content

Commit d10ee2c

Browse files
lmicciniclaude
andcommitted
Harden Redis, InstanceHA, Memcached, DNSMasq, and RabbitMQ workload security
Address anyuid SCC and overly permissive RBAC findings across all infra-operator workloads. Redis: - Remove sudo -E kolla_set_configs from startup scripts — eliminates the only blocker for nonroot-v2. Config files are now read directly from the generated path (/var/lib/config-data/generated/), and TLS certs reference the volume-mounted paths instead of /etc/pki/tls/. - Change SCC from anyuid to nonroot-v2 - Remove kolla-config volumes and mounts (config.json files deleted) - Reduce workload SA pod verbs from full CRUD to get/list/watch/patch - Add full SecurityContext via pod.RestrictiveSecurityContext helper - Set AutomountServiceAccountToken: false with projected SA token volume InstanceHA: - Change SCC from anyuid to nonroot-v2 - Add SeccompProfile RuntimeDefault - Set AutomountServiceAccountToken: false with projected SA token volume - Use lib-common pod.RestrictiveSecurityContext helper Memcached: - Bypass kolla_start entrypoint — run memcached directly by sourcing /etc/sysconfig/memcached (volume-mounted) instead of going through kolla_set_configs which requires sudo. Delete kolla config.json and remove kolla-config volume/mount. - Change SCC from anyuid to nonroot-v2 - Remove full pod CRUD from workload SA (no SA token usage) - Set AutomountServiceAccountToken: false - Add full SecurityContext via pod.RestrictiveSecurityContext helper DNSMasq: - Change SCC from anyuid to nonroot-v2 - Remove full pod CRUD from workload SA (no SA token usage) - Set AutomountServiceAccountToken: false - Add DnsmasqUID constant (42435, neutron UID from kolla) - Add full SecurityContext via pod.RestrictiveSecurityContext helper RabbitMQ: - Change SCC from anyuid to nonroot-v2 (bypasses kolla entrypoint, runs rabbitmq-server directly as UID 999, no sudo needed) - Set AutomountServiceAccountToken: false with projected SA token volume - Add full SecurityContext via pod.RestrictiveSecurityContext on both rabbitmq and amqp-proxy containers - Keep FSGroup=0 to avoid chown overhead on existing PVCs Depends-On: openstack-k8s-operators/lib-common#720 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b5154b6 commit d10ee2c

31 files changed

Lines changed: 296 additions & 264 deletions

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-20260723143954-3bd9f32306fb //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-20260723143954-3bd9f32306fb h1:J/e1Hvlt1ntRvHxUyapJ8DhsuJQZHEv8vS+a3/9FZmI=
68+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260723143954-3bd9f32306fb/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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ rules:
345345
- apiGroups:
346346
- security.openshift.io
347347
resourceNames:
348-
- anyuid
348+
- nonroot-v2
349349
resources:
350350
- securitycontextconstraints
351351
verbs:

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-20260723143954-3bd9f32306fb //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-20260723143954-3bd9f32306fb h1:J/e1Hvlt1ntRvHxUyapJ8DhsuJQZHEv8vS+a3/9FZmI=
98+
github.com/lmiccini/lib-common/modules/common v0.0.0-20260723143954-3bd9f32306fb/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/memcached/memcached_controller.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ func (r *Reconciler) GetLogger(ctx context.Context) logr.Logger {
103103
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=roles,verbs=get;list;watch;create;update;patch
104104
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebindings,verbs=get;list;watch;create;update;patch
105105
// service account permissions that are needed to grant permission to the above
106-
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=anyuid,resources=securitycontextconstraints,verbs=use
107-
// +kubebuilder:rbac:groups="",resources=pods,verbs=create;delete;get;list;patch;update;watch
106+
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=nonroot-v2,resources=securitycontextconstraints,verbs=use
108107
// +kubebuilder:rbac:groups=topology.openstack.org,resources=topologies,verbs=get;list;watch;update
109108

110109
// Reconcile - Memcached
@@ -225,15 +224,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
225224
rbacRules := []rbacv1.PolicyRule{
226225
{
227226
APIGroups: []string{"security.openshift.io"},
228-
ResourceNames: []string{"anyuid"},
227+
ResourceNames: []string{"nonroot-v2"},
229228
Resources: []string{"securitycontextconstraints"},
230229
Verbs: []string{"use"},
231230
},
232-
{
233-
APIGroups: []string{""},
234-
Resources: []string{"pods"},
235-
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
236-
},
237231
}
238232
rbacResult, err := common_rbac.ReconcileRbac(ctx, helper, instance, rbacRules)
239233
if err != nil {
@@ -496,8 +490,8 @@ func (r *Reconciler) generateConfigMaps(
496490
if instance.Spec.TLS.Enabled() {
497491
memcachedTLSListen = "| sed 's/\\(.*\\)/\\1\\nnotls:\\1:11211/'"
498492
memcachedTLSOptions = "-Z " +
499-
"-o ssl_chain_cert=/etc/pki/tls/certs/memcached.crt " +
500-
"-o ssl_key=/etc/pki/tls/private/memcached.key " +
493+
"-o ssl_chain_cert=/var/lib/config-data/tls/certs/memcached.crt " +
494+
"-o ssl_key=/var/lib/config-data/tls/private/memcached.key " +
501495
"-o ssl_ca_cert=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"
502496

503497
switch instance.Spec.TLS.MTLS.SslVerifyMode {

internal/controller/network/dnsmasq_controller.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ func (r *DNSMasqReconciler) GetLogger(ctx context.Context) logr.Logger {
9797
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=roles,verbs=get;list;watch;create;update;patch
9898
// +kubebuilder:rbac:groups="rbac.authorization.k8s.io",resources=rolebindings,verbs=get;list;watch;create;update;patch
9999
// service account permissions that are needed to grant permission to the above
100-
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=anyuid,resources=securitycontextconstraints,verbs=use
101-
// +kubebuilder:rbac:groups="",resources=pods,verbs=create;delete;get;list;patch;update;watch
100+
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=nonroot-v2,resources=securitycontextconstraints,verbs=use
102101
// +kubebuilder:rbac:groups=topology.openstack.org,resources=topologies,verbs=get;list;watch;update
103102

104103
// Reconcile is part of the main kubernetes reconciliation loop which aims to
@@ -205,15 +204,10 @@ func (r *DNSMasqReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
205204
rbacRules := []rbacv1.PolicyRule{
206205
{
207206
APIGroups: []string{"security.openshift.io"},
208-
ResourceNames: []string{"anyuid"},
207+
ResourceNames: []string{"nonroot-v2"},
209208
Resources: []string{"securitycontextconstraints"},
210209
Verbs: []string{"use"},
211210
},
212-
{
213-
APIGroups: []string{""},
214-
Resources: []string{"pods"},
215-
Verbs: []string{"create", "get", "list", "watch", "update", "patch", "delete"},
216-
},
217211
}
218212
rbacResult, err := common_rbac.ReconcileRbac(ctx, helper, instance, rbacRules)
219213
if err != nil {

internal/controller/rabbitmq/proxy.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
rabbitmqv1beta1 "github.com/openstack-k8s-operators/infra-operator/apis/rabbitmq/v1beta1"
99
"github.com/openstack-k8s-operators/infra-operator/internal/rabbitmq"
1010
"github.com/openstack-k8s-operators/lib-common/modules/common/helper"
11+
"github.com/openstack-k8s-operators/lib-common/modules/common/pod"
1112
corev1 "k8s.io/api/core/v1"
1213
"k8s.io/apimachinery/pkg/api/resource"
1314
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -153,12 +154,11 @@ func BuildProxySidecarContainer(instance *rabbitmqv1beta1.RabbitMq, IPv6Enabled
153154
TimeoutSeconds: 3,
154155
FailureThreshold: 3,
155156
},
156-
SecurityContext: &corev1.SecurityContext{
157-
AllowPrivilegeEscalation: ptr.To(false),
158-
Capabilities: &corev1.Capabilities{
159-
Drop: []corev1.Capability{"ALL"},
160-
},
161-
},
157+
SecurityContext: func() *corev1.SecurityContext {
158+
sc := pod.RestrictiveSecurityContext(999)
159+
sc.ReadOnlyRootFilesystem = ptr.To(false)
160+
return sc
161+
}(),
162162
}
163163

164164
// Mount TLS certificates if TLS is enabled

internal/controller/rabbitmq/rabbitmq_controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ type Reconciler struct {
137137
// Required to manage PodDisruptionBudgets for multi-replica deployments
138138
// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete
139139

140+
// Required to grant nonroot-v2 SCC to RabbitMQ workload SA
141+
// +kubebuilder:rbac:groups="security.openshift.io",resourceNames=nonroot-v2,resources=securitycontextconstraints,verbs=use
142+
140143
// Required to create per-pod LoadBalancer services
141144
// +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete
142145

@@ -577,7 +580,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ct
577580
rbacRules := []rbacv1.PolicyRule{
578581
{
579582
APIGroups: []string{"security.openshift.io"},
580-
ResourceNames: []string{"anyuid"},
583+
ResourceNames: []string{"nonroot-v2"},
581584
Resources: []string{"securitycontextconstraints"},
582585
Verbs: []string{"use"},
583586
},

0 commit comments

Comments
 (0)