Skip to content

Commit ef0900a

Browse files
committed
network: RBAC for the frr-k8s static pod node credentials
The static pod authenticates with the node kubeconfig, i.e. the MCO node-bootstrapper ServiceAccount. Grant it the reads the stock frr-k8s controller's informers require plus write access to the node state CRs, only when BGP VIP management is active. Per-node write scoping is not expressible in RBAC; admission-level enforcement is planned before GA.
1 parent 1dd7ce1 commit ef0900a

2 files changed

Lines changed: 122 additions & 9 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
{{ if .BGPVIPManagement }}
2+
# RBAC for the frr-k8s static pod on control plane nodes (BGP VIP
3+
# management). The static pod authenticates with the node kubeconfig, whose
4+
# identity is the MCO node-bootstrapper ServiceAccount.
5+
apiVersion: rbac.authorization.k8s.io/v1
6+
kind: ClusterRole
7+
metadata:
8+
name: frr-k8s-static-pod
9+
rules:
10+
- apiGroups:
11+
- frrk8s.metallb.io
12+
resources:
13+
- frrconfigurations
14+
- frrk8sconfigurations
15+
verbs:
16+
- get
17+
- list
18+
- watch
19+
# No delete: stale state objects are removed by frr-k8s's status cleaner.
20+
- apiGroups:
21+
- frrk8s.metallb.io
22+
resources:
23+
- frrnodestates
24+
- bgpsessionstates
25+
verbs:
26+
- get
27+
- list
28+
- watch
29+
- create
30+
- update
31+
- patch
32+
- apiGroups:
33+
- frrk8s.metallb.io
34+
resources:
35+
- frrnodestates/status
36+
- bgpsessionstates/status
37+
verbs:
38+
- get
39+
- update
40+
- patch
41+
- apiGroups:
42+
- ""
43+
resources:
44+
- nodes
45+
verbs:
46+
- get
47+
- list
48+
- watch
49+
---
50+
apiVersion: rbac.authorization.k8s.io/v1
51+
kind: ClusterRoleBinding
52+
metadata:
53+
name: frr-k8s-static-pod
54+
roleRef:
55+
apiGroup: rbac.authorization.k8s.io
56+
kind: ClusterRole
57+
name: frr-k8s-static-pod
58+
subjects:
59+
- kind: ServiceAccount
60+
name: node-bootstrapper
61+
namespace: openshift-machine-config-operator
62+
---
63+
# Read-only subset of the DaemonSet SA's Role in 002-rbac.yaml.
64+
apiVersion: rbac.authorization.k8s.io/v1
65+
kind: Role
66+
metadata:
67+
name: frr-k8s-static-pod
68+
namespace: openshift-frr-k8s
69+
rules:
70+
- apiGroups:
71+
- ""
72+
resources:
73+
- secrets
74+
verbs:
75+
- get
76+
- list
77+
- watch
78+
- apiGroups:
79+
- ""
80+
resources:
81+
- pods
82+
verbs:
83+
- get
84+
- list
85+
- watch
86+
---
87+
apiVersion: rbac.authorization.k8s.io/v1
88+
kind: RoleBinding
89+
metadata:
90+
name: frr-k8s-static-pod
91+
namespace: openshift-frr-k8s
92+
roleRef:
93+
apiGroup: rbac.authorization.k8s.io
94+
kind: Role
95+
name: frr-k8s-static-pod
96+
subjects:
97+
- kind: ServiceAccount
98+
name: node-bootstrapper
99+
namespace: openshift-machine-config-operator
100+
{{ end }}

pkg/network/render_test.go

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -610,10 +610,9 @@ func Test_renderAdditionalRoutingCapabilities(t *testing.T) {
610610
}
611611
}
612612

613-
// Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement verifies the
614-
// frr-k8s DaemonSet affinity: with BGP VIP management active the DaemonSet
615-
// must avoid control plane nodes by role (the static FRR pods own them);
616-
// otherwise no affinity is rendered at all.
613+
// Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement verifies that the
614+
// master-avoiding DaemonSet affinity and the static pod RBAC render only
615+
// under BGP VIP management.
617616
func Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement(t *testing.T) {
618617
g := NewGomegaWithT(t)
619618

@@ -637,9 +636,7 @@ func Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement(t *testing.T) {
637636
},
638637
},
639638
}
640-
// The Infrastructure vipManagement status field is not in the vendored
641-
// openshift/api yet (openshift/api#2923), so seed it unstructured -
642-
// exactly the shape isBGPVIPManagement reads.
639+
// vipManagement is not in the vendored openshift/api yet (#2923).
643640
infra := &uns.Unstructured{}
644641
infra.SetGroupVersionKind(schema.GroupVersionKind{Group: "config.openshift.io", Version: "v1", Kind: "Infrastructure"})
645642
infra.SetName("cluster")
@@ -666,7 +663,7 @@ func Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement(t *testing.T) {
666663

667664
got, err := renderAdditionalRoutingCapabilities(operConf, manifestDir, bgpVIP)
668665
g.Expect(err).NotTo(HaveOccurred())
669-
g.Expect(got).To(HaveLen(21))
666+
g.Expect(got).To(HaveLen(25))
670667
affinity, found := daemonSetAffinity(got)
671668
g.Expect(found).To(BeTrue())
672669
terms, found, err := uns.NestedSlice(affinity, "nodeAffinity", "requiredDuringSchedulingIgnoredDuringExecution", "nodeSelectorTerms")
@@ -680,7 +677,17 @@ func Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement(t *testing.T) {
680677
"operator": "DoesNotExist",
681678
}))
682679

683-
// BGP VIP management inactive (nil bootstrap result): no affinity.
680+
staticPodRBAC := func(objs []*uns.Unstructured) bool {
681+
for _, obj := range objs {
682+
if obj.GetName() == "frr-k8s-static-pod" {
683+
return true
684+
}
685+
}
686+
return false
687+
}
688+
g.Expect(staticPodRBAC(got)).To(BeTrue())
689+
690+
// Inactive (nil bootstrap result): no affinity, no static pod RBAC.
684691
bgpVIP, err = isBGPVIPManagement(client, nil, featureGates)
685692
g.Expect(err).NotTo(HaveOccurred())
686693
g.Expect(bgpVIP).To(BeFalse())
@@ -689,4 +696,10 @@ func Test_renderAdditionalRoutingCapabilitiesBGPVIPManagement(t *testing.T) {
689696
g.Expect(got).To(HaveLen(21))
690697
_, found = daemonSetAffinity(got)
691698
g.Expect(found).To(BeFalse())
699+
g.Expect(staticPodRBAC(got)).To(BeFalse())
700+
701+
// BGP VIP management without the FRR provider is a configuration error:
702+
// the FRRConfiguration CRD ships with the frr-k8s bundle.
703+
_, err = renderBGPVIPFRRConfiguration(&operv1.NetworkSpec{}, client, true)
704+
g.Expect(err).To(MatchError(ContainSubstring("requires the FRR additional routing capability provider")))
692705
}

0 commit comments

Comments
 (0)