Skip to content

Commit a825cbc

Browse files
CoderYellowclaude
andcommitted
feat: centrally managed connections via cluster-scoped ClusterConnection CRD
Namespaced resources can now reference a ClusterConnection (spec.connection. clusterRef) instead of a per-namespace connection secret. The admin credentials Secret lives only in the operator's namespace and allowedNamespaces restricts which namespaces may use the connection. - ConnectionRef: secretRef and clusterRef, exactly one required - chart: clusterconnections RBAC, optional rbac.clusterWideSecrets, plus a namespaced Role for operator-namespace secrets - e2e: clusterRef bucket converges; disallowed namespace is rejected Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent ec4b733 commit a825cbc

15 files changed

Lines changed: 621 additions & 98 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rustfs-operator"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2024"
55
rust-version = "1.92"
66
license = "MIT OR Apache-2.0"

README.md

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,40 @@ the [`rc-core`](https://crates.io/crates/rc-core) /
88

99
## CRDs (`rustfs.com/v1alpha1`)
1010

11-
| Kind | Short name | Manages |
12-
|----------|------------|-----------------------------------------------------|
13-
| `Bucket` | `rfb` | bucket existence, versioning, hard quota |
14-
| `User` | `rfu` | IAM user, enabled/disabled, attached policies |
15-
| `Policy` | `rfp` | IAM policy document (inline YAML/JSON) |
11+
| Kind | Short name | Scope | Manages |
12+
|---------------------|------------|------------|-----------------------------------------------|
13+
| `Bucket` | `rfb` | namespaced | bucket existence, versioning, hard quota |
14+
| `User` | `rfu` | namespaced | IAM user, enabled/disabled, attached policies |
15+
| `Policy` | `rfp` | namespaced | IAM policy document (inline YAML/JSON) |
16+
| `ClusterConnection` | `rfcc` | cluster | centrally managed RustFS server connection |
1617

17-
Every resource references a connection Secret in its own namespace:
18+
Namespaced resources select a RustFS server via `spec.connection`, in one of
19+
two mutually exclusive ways:
20+
21+
**Centrally managed (recommended for multi-tenant clusters)** — reference a
22+
cluster-scoped `ClusterConnection`; the admin credentials Secret lives only
23+
in the operator's namespace, and `allowedNamespaces` restricts who may use
24+
it:
25+
26+
```yaml
27+
spec:
28+
connection:
29+
clusterRef: prod
30+
```
31+
32+
**Self-service** — reference a connection Secret in the resource's own
33+
namespace (keys: `endpoint`, `accessKey`, `secretKey`; optional `region`,
34+
`insecure`):
1835

1936
```yaml
20-
stringData:
21-
endpoint: http://rustfs.storage.svc:9000
22-
accessKey: rustfsadmin
23-
secretKey: rustfsadmin
24-
# region: us-east-1 # optional
25-
# insecure: "true" # optional
37+
spec:
38+
connection:
39+
secretRef: rustfs-conn
2640
```
2741

28-
See `deploy/example.yaml` for a complete example. Each resource supports
29-
`deletionPolicy: Delete` (default; the remote resource is removed via
30-
finalizer when the CR is deleted) or `Retain`.
42+
See `deploy/example.yaml` for complete examples of both. Each resource
43+
supports `deletionPolicy: Delete` (default; the remote resource is removed
44+
via finalizer when the CR is deleted) or `Retain`.
3145

3246
## Install
3347

charts/rustfs-operator/crds/crds.yaml

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,20 @@ spec:
3333
nullable: true
3434
type: string
3535
connection:
36-
description: Reference to the connection Secret (same namespace as the resource).
36+
description: |-
37+
Reference to a RustFS server. Exactly one of `secretRef` / `clusterRef`
38+
must be set (validated at reconcile time).
3739
properties:
40+
clusterRef:
41+
description: Name of a cluster-scoped ClusterConnection.
42+
nullable: true
43+
type: string
3844
secretRef:
39-
description: Name of the Secret holding `endpoint`, `accessKey` and `secretKey`.
45+
description: |-
46+
Name of a Secret in the resource's namespace holding `endpoint`,
47+
`accessKey` and `secretKey` (optional: `region`, `insecure`).
48+
nullable: true
4049
type: string
41-
required:
42-
- secretRef
4350
type: object
4451
deletionPolicy:
4552
default: Delete
@@ -123,13 +130,20 @@ spec:
123130
nullable: true
124131
type: string
125132
connection:
126-
description: Reference to the connection Secret (same namespace as the resource).
133+
description: |-
134+
Reference to a RustFS server. Exactly one of `secretRef` / `clusterRef`
135+
must be set (validated at reconcile time).
127136
properties:
137+
clusterRef:
138+
description: Name of a cluster-scoped ClusterConnection.
139+
nullable: true
140+
type: string
128141
secretRef:
129-
description: Name of the Secret holding `endpoint`, `accessKey` and `secretKey`.
142+
description: |-
143+
Name of a Secret in the resource's namespace holding `endpoint`,
144+
`accessKey` and `secretKey` (optional: `region`, `insecure`).
145+
nullable: true
130146
type: string
131-
required:
132-
- secretRef
133147
type: object
134148
deletionPolicy:
135149
default: Delete
@@ -225,13 +239,20 @@ spec:
225239
description: An IAM policy in RustFS.
226240
properties:
227241
connection:
228-
description: Reference to the connection Secret (same namespace as the resource).
242+
description: |-
243+
Reference to a RustFS server. Exactly one of `secretRef` / `clusterRef`
244+
must be set (validated at reconcile time).
229245
properties:
246+
clusterRef:
247+
description: Name of a cluster-scoped ClusterConnection.
248+
nullable: true
249+
type: string
230250
secretRef:
231-
description: Name of the Secret holding `endpoint`, `accessKey` and `secretKey`.
251+
description: |-
252+
Name of a Secret in the resource's namespace holding `endpoint`,
253+
`accessKey` and `secretKey` (optional: `region`, `insecure`).
254+
nullable: true
232255
type: string
233-
required:
234-
- secretRef
235256
type: object
236257
deletionPolicy:
237258
default: Delete
@@ -278,3 +299,69 @@ spec:
278299
storage: true
279300
subresources:
280301
status: {}
302+
---
303+
apiVersion: apiextensions.k8s.io/v1
304+
kind: CustomResourceDefinition
305+
metadata:
306+
name: clusterconnections.rustfs.com
307+
spec:
308+
group: rustfs.com
309+
names:
310+
categories: []
311+
kind: ClusterConnection
312+
plural: clusterconnections
313+
shortNames:
314+
- rfcc
315+
singular: clusterconnection
316+
scope: Cluster
317+
versions:
318+
- additionalPrinterColumns:
319+
- jsonPath: .spec.endpoint
320+
name: Endpoint
321+
type: string
322+
name: v1alpha1
323+
schema:
324+
openAPIV3Schema:
325+
description: Auto-generated derived type for ClusterConnectionSpec via `CustomResource`
326+
properties:
327+
spec:
328+
description: |-
329+
A centrally managed connection to a RustFS server. Cluster-scoped; the
330+
referenced credentials Secret lives in the operator's own namespace, so
331+
application namespaces never see admin credentials.
332+
properties:
333+
allowedNamespaces:
334+
description: |-
335+
Namespaces whose resources may use this connection.
336+
Absent means all namespaces are allowed.
337+
items:
338+
type: string
339+
nullable: true
340+
type: array
341+
credentialsSecretRef:
342+
description: |-
343+
Name of the Secret in the operator's namespace holding `accessKey`
344+
and `secretKey`.
345+
type: string
346+
endpoint:
347+
description: RustFS endpoint URL, e.g. `http://rustfs.storage.svc:9000`.
348+
type: string
349+
insecure:
350+
default: false
351+
description: Skip TLS verification.
352+
type: boolean
353+
region:
354+
description: AWS region; defaults to `us-east-1`.
355+
nullable: true
356+
type: string
357+
required:
358+
- credentialsSecretRef
359+
- endpoint
360+
type: object
361+
required:
362+
- spec
363+
title: ClusterConnection
364+
type: object
365+
served: true
366+
storage: true
367+
subresources: {}

charts/rustfs-operator/templates/rbac.yaml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ rules:
1212
- apiGroups: ["rustfs.com"]
1313
resources: ["buckets/status", "users/status", "policies/status"]
1414
verbs: ["get", "patch", "update"]
15-
# connection and user-credential secrets
15+
- apiGroups: ["rustfs.com"]
16+
resources: ["clusterconnections"]
17+
verbs: ["get", "list", "watch"]
18+
{{- if .Values.rbac.clusterWideSecrets }}
19+
# needed for connection.secretRef (per-namespace connection secrets);
20+
# set rbac.clusterWideSecrets=false if you only use ClusterConnection
1621
- apiGroups: [""]
1722
resources: ["secrets"]
1823
verbs: ["get"]
24+
{{- end }}
1925
---
2026
apiVersion: rbac.authorization.k8s.io/v1
2127
kind: ClusterRoleBinding
@@ -31,4 +37,34 @@ subjects:
3137
- kind: ServiceAccount
3238
name: {{ include "rustfs-operator.serviceAccountName" . }}
3339
namespace: {{ .Release.Namespace }}
40+
---
41+
# Secrets in the operator's own namespace (ClusterConnection credentials)
42+
# are always readable, regardless of rbac.clusterWideSecrets.
43+
apiVersion: rbac.authorization.k8s.io/v1
44+
kind: Role
45+
metadata:
46+
name: {{ include "rustfs-operator.fullname" . }}
47+
namespace: {{ .Release.Namespace }}
48+
labels:
49+
{{- include "rustfs-operator.labels" . | nindent 4 }}
50+
rules:
51+
- apiGroups: [""]
52+
resources: ["secrets"]
53+
verbs: ["get"]
54+
---
55+
apiVersion: rbac.authorization.k8s.io/v1
56+
kind: RoleBinding
57+
metadata:
58+
name: {{ include "rustfs-operator.fullname" . }}
59+
namespace: {{ .Release.Namespace }}
60+
labels:
61+
{{- include "rustfs-operator.labels" . | nindent 4 }}
62+
roleRef:
63+
apiGroup: rbac.authorization.k8s.io
64+
kind: Role
65+
name: {{ include "rustfs-operator.fullname" . }}
66+
subjects:
67+
- kind: ServiceAccount
68+
name: {{ include "rustfs-operator.serviceAccountName" . }}
69+
namespace: {{ .Release.Namespace }}
3470
{{- end }}

charts/rustfs-operator/values.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ serviceAccount:
1616

1717
rbac:
1818
create: true
19+
# Grants cluster-wide Secret read. Needed for `connection.secretRef`
20+
# (per-namespace connection secrets) and for `User.spec.secretKeyRef`
21+
# Secrets in application namespaces. Set to false for the strictest setup:
22+
# everything uses `connection.clusterRef` and User resources live in the
23+
# operator's namespace; the operator then only reads Secrets there.
24+
clusterWideSecrets: true
1925

2026
# value for RUST_LOG
2127
logLevel: info

0 commit comments

Comments
 (0)