Skip to content

Commit 9c6a40a

Browse files
committed
Add OVN RBAC support with per-node ovn-controller certificates
Enable OVN role-based access control (RBAC) on the Southbound database so that ovn-controller nodes can only modify their own chassis rows. When the openstack-operator provides an RBAC issuer name (from a dedicated rootca-ovn-rbac CA, see patch [1]), this patch: * Creates per-node cert-manager Certificate CRs for each ovn-controller pod, with CN set to a deterministic UUID5 system-id derived from the node name (ComputeSystemID). This CN must match the chassis system-id for RBAC to authorize operations. * Copies the RBAC client cert/key into /etc/openvswitch/ on each node via the config job, and switches ovn-controller to use these dedicated paths instead of the shared OVN DB cert. * Mounts the RBAC CA certificate into ovsdbserver-sb pods and builds a combined CA bundle (regular CA + RBAC CA) so the SB database can verify ovn-controller client certificates. * Sets role=ovn-controller on the SB DB connection (port 6642) to enforce RBAC. * Creates a second SB DB listener on port 16642 with full (unrestricted) access, used by ovn-northd. * Updates inactivity probe handling in setup.sh and runtime-config.sh to iterate over all connections, since SB now has two listeners. * ovn-controller POD now waits for the Northd to be ready before start, it is done to avoid race condition when ovn-controller POD could be started before Northd would populate RBAC rules in the SB DB and that could cause issues with connection of the ovn-controller to the SB DB. * remove OVS DaemonSet readiness gate in the ovncontroller controller - it was there to make sure that local ovsdb is up so that config job would be able to store config values in it. But init scripts are already waiting actively for the ovsdb to become active before it anything else will be done. This check is also causing deadlock with deploying ovs and ovn-controller PODs now with RBAC enabled as ovn-controller needs to have certificates ready to start and create br-int brigde. That can't be done if the config job is not started and config job couldn't be started because ovncontroller controller was waiting for the OVS DaemonSet to be ready. * Remove OVS DaemonSet readiness gate from the ovncontroller controller - the gate ensured that the local ovsdb was running before the config job attempted to store configuration values. However, the init scripts already poll for ovsdb availability before doing anything else, making the gate redundant. With RBAC enabled, this gate also causes a deadlock: ovn-controller needs its RBAC certificates to start and create the br-int bridge, but those certificates are deployed by the config job, which cannot run until the OVS DaemonSet is ready — and the OVS DaemonSet cannot become ready without br-int. [1] openstack-k8s-operators/openstack-operator#1906 Related: #OSPRH-1921 Closes: #OSPRH-1922 Assisted-by: claude-opus-4.6 Signed-off-by: Slawek Kaplonski <skaplons@redhat.com>
1 parent d009c70 commit 9c6a40a

31 files changed

Lines changed: 867 additions & 43 deletions

api/bases/ovn.openstack.org_ovncontrollers.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ spec:
150150
description: Image used for the ovn-controller container (will be
151151
set to environmental default if empty)
152152
type: string
153+
ovnIssuerName:
154+
description: |-
155+
OvnIssuerName - The name of the cert-manager Issuer used to sign
156+
per-node ovn-controller certificates. When set, the controller
157+
creates cert-manager Certificate resources for each node with
158+
CN matching the chassis system-id for OVN RBAC.
159+
type: string
153160
ovnLogLevel:
154161
default: info
155162
description: OVNLogLevel - Set log level off, emer, err, warn, info

api/bases/ovn.openstack.org_ovndbclusters.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ spec:
438438
description: InternalDBAddress - DB IP address used by other Pods
439439
in the cluster
440440
type: string
441+
internalDbAddressRbacFullAccess:
442+
description: |-
443+
InternalDBAddressRbacFullAccess - DB IP address for full-access (non-RBAC)
444+
connections, used by ovn-northd when RBAC is enabled on SB DB
445+
type: string
441446
lastAppliedTopology:
442447
description: LastAppliedTopology - the last applied Topology
443448
properties:

api/v1beta1/client.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ func GetOVNController(
7272
return nil, nil
7373
}
7474

75+
// GetOVNNorthd - return OVNNorthd instance in the given namespace
76+
func GetOVNNorthd(
77+
ctx context.Context,
78+
h *helper.Helper,
79+
namespace string,
80+
) (*OVNNorthd, error) {
81+
ovnNorthdList := &OVNNorthdList{}
82+
listOpts := []client.ListOption{
83+
client.InNamespace(namespace),
84+
}
85+
err := h.GetClient().List(ctx, ovnNorthdList, listOpts...)
86+
if err != nil {
87+
return nil, err
88+
}
89+
if len(ovnNorthdList.Items) > 0 {
90+
return &ovnNorthdList.Items[0], nil
91+
}
92+
93+
return nil, nil
94+
}
95+
7596
// GetDBClusterByType - return OVNDBCluster for the given dbType
7697
func GetDBClusterByType(
7798
ctx context.Context,

api/v1beta1/ovncontroller_types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ type OVNControllerSpecCore struct {
124124
// +kubebuilder:validation:Optional
125125
// MetricsTLS - Parameters related to TLS for metrics daemonset
126126
MetricsTLS tls.SimpleService `json:"metricsTLS,omitempty"`
127+
128+
// +kubebuilder:validation:Optional
129+
// OvnIssuerName - The name of the cert-manager Issuer used to sign
130+
// per-node ovn-controller certificates. When set, the controller
131+
// creates cert-manager Certificate resources for each node with
132+
// CN matching the chassis system-id for OVN RBAC.
133+
OvnIssuerName string `json:"ovnIssuerName,omitempty"`
127134
}
128135

129136
// OVNControllerStatus defines the observed state of OVNController

api/v1beta1/ovndbcluster_types.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ type OVNDBClusterStatus struct {
171171
// InternalDBAddress - DB IP address used by other Pods in the cluster
172172
InternalDBAddress string `json:"internalDbAddress,omitempty"`
173173

174+
// InternalDBAddressRbacFullAccess - DB IP address for full-access (non-RBAC)
175+
// connections, used by ovn-northd when RBAC is enabled on SB DB
176+
InternalDBAddressRbacFullAccess string `json:"internalDbAddressRbacFullAccess,omitempty"`
177+
174178
// NetworkAttachments status of the deployment pods
175179
NetworkAttachments map[string][]string `json:"networkAttachments,omitempty"`
176180

@@ -239,6 +243,18 @@ func (instance OVNDBCluster) GetInternalEndpoint() (string, error) {
239243
return instance.Status.InternalDBAddress, nil
240244
}
241245

246+
// GetInternalEndpointRbacFullAccess - return the full-access (non-RBAC) internal endpoint for SB DB.
247+
// Falls back to GetInternalEndpoint if the DB is not SB or TLS is not enabled.
248+
func (instance OVNDBCluster) GetInternalEndpointRbacFullAccess() (string, error) {
249+
if instance.Spec.DBType != SBDBType || !instance.Spec.TLS.Enabled() {
250+
return instance.GetInternalEndpoint()
251+
}
252+
if instance.Status.InternalDBAddressRbacFullAccess == "" {
253+
return "", fmt.Errorf("internal RBAC full-access DBEndpoint not ready yet for %s", instance.Spec.DBType)
254+
}
255+
return instance.Status.InternalDBAddressRbacFullAccess, nil
256+
}
257+
242258
// GetExternalEndpoint - return the DNS that openstack dnsmasq can resolve
243259
func (instance OVNDBCluster) GetExternalEndpoint() (string, error) {
244260
if (instance.Spec.NetworkAttachment != "" || instance.Spec.Override.Service != nil) && instance.Status.DBAddress == "" {

cmd/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646

4747
// +kubebuilder:scaffold:imports
4848

49+
certmgrv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
4950
networkv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
5051
infranetworkv1 "github.com/openstack-k8s-operators/infra-operator/apis/network/v1beta1"
5152
topologyv1 "github.com/openstack-k8s-operators/infra-operator/apis/topology/v1beta1"
@@ -66,6 +67,7 @@ func init() {
6667
utilruntime.Must(networkv1.AddToScheme(scheme))
6768
utilruntime.Must(infranetworkv1.AddToScheme(scheme))
6869
utilruntime.Must(topologyv1.AddToScheme(scheme))
70+
utilruntime.Must(certmgrv1.AddToScheme(scheme))
6971
//+kubebuilder:scaffold:scheme
7072
}
7173

config/crd/bases/ovn.openstack.org_ovncontrollers.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ spec:
150150
description: Image used for the ovn-controller container (will be
151151
set to environmental default if empty)
152152
type: string
153+
ovnIssuerName:
154+
description: |-
155+
OvnIssuerName - The name of the cert-manager Issuer used to sign
156+
per-node ovn-controller certificates. When set, the controller
157+
creates cert-manager Certificate resources for each node with
158+
CN matching the chassis system-id for OVN RBAC.
159+
type: string
153160
ovnLogLevel:
154161
default: info
155162
description: OVNLogLevel - Set log level off, emer, err, warn, info

config/crd/bases/ovn.openstack.org_ovndbclusters.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,11 @@ spec:
438438
description: InternalDBAddress - DB IP address used by other Pods
439439
in the cluster
440440
type: string
441+
internalDbAddressRbacFullAccess:
442+
description: |-
443+
InternalDBAddressRbacFullAccess - DB IP address for full-access (non-RBAC)
444+
connections, used by ovn-northd when RBAC is enabled on SB DB
445+
type: string
441446
lastAppliedTopology:
442447
description: LastAppliedTopology - the last applied Topology
443448
properties:

config/rbac/role.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,26 @@ rules:
6565
- patch
6666
- update
6767
- watch
68+
- apiGroups:
69+
- cert-manager.io
70+
resources:
71+
- certificates
72+
verbs:
73+
- create
74+
- delete
75+
- get
76+
- list
77+
- patch
78+
- update
79+
- watch
80+
- apiGroups:
81+
- cert-manager.io
82+
resources:
83+
- issuers
84+
verbs:
85+
- get
86+
- list
87+
- watch
6888
- apiGroups:
6989
- k8s.cni.cncf.io
7090
resources:

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ module github.com/openstack-k8s-operators/ovn-operator
33
go 1.24.4
44

55
require (
6+
github.com/cert-manager/cert-manager v1.16.5
67
github.com/go-logr/logr v1.4.3
78
github.com/google/uuid v1.6.0
89
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.7
910
github.com/onsi/ginkgo/v2 v2.28.2
1011
github.com/onsi/gomega v1.40.0
1112
github.com/openstack-k8s-operators/infra-operator/apis v0.6.1-0.20260508091801-73f228e6af31
13+
github.com/openstack-k8s-operators/lib-common/modules/certmanager v0.6.1-0.20260506154724-30a976ba8ef0
1214
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.1-0.20260506154724-30a976ba8ef0
1315
github.com/openstack-k8s-operators/lib-common/modules/test v0.6.1-0.20260506154724-30a976ba8ef0
1416
github.com/openstack-k8s-operators/ovn-operator/api v0.0.0-20230418071801-b5843d9e05fb
@@ -65,7 +67,7 @@ require (
6567
github.com/prometheus/procfs v0.16.1 // indirect
6668
github.com/spf13/cobra v1.9.1 // indirect
6769
github.com/spf13/pflag v1.0.7 // indirect
68-
github.com/stoewer/go-strcase v1.2.0 // indirect
70+
github.com/stoewer/go-strcase v1.3.0 // indirect
6971
github.com/x448/float16 v0.8.4 // indirect
7072
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
7173
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
@@ -103,6 +105,7 @@ require (
103105
k8s.io/klog/v2 v2.130.1 // indirect
104106
k8s.io/kube-openapi v0.0.0-20250902184714-7fc278399c7f // indirect
105107
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect
108+
sigs.k8s.io/gateway-api v1.1.0 // indirect
106109
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
107110
sigs.k8s.io/randfill v1.0.0 // indirect
108111
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect

0 commit comments

Comments
 (0)