Skip to content

Commit 31c976c

Browse files
committed
Add better SA auth handling
On-behalf-of: SAP <[email protected]> Signed-off-by: Mangirdas Judeikis <[email protected]>
1 parent 8599665 commit 31c976c

26 files changed

+970
-25
lines changed

DEVELOPMENT.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,60 @@ The project has been initialised with:
1414
```sh
1515
$ kubebuilder init --domain operator.kcp.io --owner "The KCP Authors" --project-name kcp-operator
1616
```
17+
18+
19+
## Kind setup
20+
21+
```sh
22+
kind create cluster
23+
```
24+
25+
Install cert-manager:
26+
27+
```sh
28+
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml
29+
```
30+
31+
Install etcd:
32+
33+
```sh
34+
helm install etcd oci://registry-1.docker.io/bitnamicharts/etcd --set auth.rbac.enabled=false --set auth.rbac.create=false
35+
helm install etcd-shard oci://registry-1.docker.io/bitnamicharts/etcd --set auth.rbac.enabled=false --set auth.rbac.create=false
36+
```
37+
38+
Create issuer:
39+
40+
```sh
41+
kubectl apply -f config/samples/cert-manager/issuer.yaml
42+
```
43+
44+
Build the image:
45+
46+
```sh
47+
make docker-build IMG=ghcr.io/kcp-dev/kcp-operator:1
48+
kind load docker-image ghcr.io/kcp-dev/kcp-operator:1
49+
```
50+
51+
Load the image into the kind cluster:
52+
53+
```sh
54+
make deploy IMG=ghcr.io/kcp-dev/kcp-operator:1
55+
```
56+
57+
Now you can create a root shard:
58+
59+
```sh
60+
kubectl apply -f config/samples/operator.kcp.io_v1alpha1_rootshard.yaml
61+
```
62+
63+
Shards:
64+
65+
```sh
66+
kubectl apply -f config/samples/operator.kcp.io_v1alpha1_shard.yaml
67+
```
68+
69+
Frontproxy:
70+
71+
```sh
72+
kubectl apply -f config/samples/operator.kcp.io_v1alpha1_frontproxy.yaml
73+
```

config/crd/bases/operator.kcp.io_frontproxies.yaml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ spec:
8181
and Authorization for this front-proxy instance.'
8282
properties:
8383
dropGroups:
84-
description: 'Optional: DropGroups configures groups to be dropped
85-
before forwarding requests to Shards'
84+
description: |-
85+
Optional: DropGroups configures groups to be dropped before forwarding requests to Shards.
86+
If set, it will mount every shard's service account certificate to the front-proxy.
8687
items:
8788
type: string
8889
type: array
@@ -131,6 +132,17 @@ spec:
131132
items:
132133
type: string
133134
type: array
135+
serviceAccount:
136+
description: 'Optional: serviceAccountAuthentication configures
137+
ServiceAccount Authentication.'
138+
properties:
139+
enabled:
140+
description: 'Optional: Enabled enables or disables ServiceAccount
141+
Authentication.'
142+
type: boolean
143+
required:
144+
- enabled
145+
type: object
134146
type: object
135147
certificateTemplates:
136148
additionalProperties:

config/crd/bases/operator.kcp.io_rootshards.yaml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,74 @@ spec:
120120
type: string
121121
type: object
122122
type: object
123+
auth:
124+
description: 'Optional: Auth configures various aspects of Authentication
125+
and Authorization for this shard.'
126+
properties:
127+
dropGroups:
128+
description: |-
129+
Optional: DropGroups configures groups to be dropped before forwarding requests to Shards.
130+
If set, it will mount every shard's service account certificate to the front-proxy.
131+
items:
132+
type: string
133+
type: array
134+
oidc:
135+
description: 'Optional: OIDC configures OpenID Connect Authentication.'
136+
properties:
137+
clientID:
138+
description: ClientID is the OIDC client ID configured on
139+
the issuer side for this KCP instance.
140+
type: string
141+
clientSecret:
142+
description: |-
143+
Optionally provide the client secret for the OIDC client. This is not used by KCP itself, but is used to generate
144+
a OIDC kubeconfig that can be shared with users to log in via the OIDC provider.
145+
type: string
146+
groupsClaim:
147+
description: 'Experimental: Optionally provides a custom claim
148+
for fetching groups. The claim must be a string or an array
149+
of strings.'
150+
type: string
151+
groupsPrefix:
152+
description: |-
153+
Optionally sets a custom groups prefix. This defaults to "oidc:" if unset, which means a group called "group1"
154+
on the OIDC side will be recognised as "oidc:group1" in KCP.
155+
type: string
156+
issuerURL:
157+
description: IssuerURL is used for the OIDC issuer URL. Only
158+
https URLs will be accepted.
159+
type: string
160+
usernameClaim:
161+
description: Optionally uses a custom claim for fetching the
162+
username. This defaults to "sub" if unset.
163+
type: string
164+
usernamePrefix:
165+
description: |-
166+
Optionally sets a custom username prefix. This defaults to "oidc:" if unset, which means a user called "[email protected]"
167+
on the OIDC side will be recognised as "oidc:[email protected]" in KCP.
168+
type: string
169+
required:
170+
- clientID
171+
- issuerURL
172+
type: object
173+
passOnGroups:
174+
description: 'Optional: PassOnGroups configures groups to be passed
175+
on before forwarding requests to Shards'
176+
items:
177+
type: string
178+
type: array
179+
serviceAccount:
180+
description: 'Optional: serviceAccountAuthentication configures
181+
ServiceAccount Authentication.'
182+
properties:
183+
enabled:
184+
description: 'Optional: Enabled enables or disables ServiceAccount
185+
Authentication.'
186+
type: boolean
187+
required:
188+
- enabled
189+
type: object
190+
type: object
123191
authorization:
124192
properties:
125193
webhook:
@@ -1742,6 +1810,21 @@ spec:
17421810
x-kubernetes-list-type: map
17431811
phase:
17441812
type: string
1813+
shards:
1814+
description: Shards is a list of shards that are currently registered
1815+
with this root shard.
1816+
items:
1817+
properties:
1818+
name:
1819+
description: Name is the name of the shard.
1820+
type: string
1821+
required:
1822+
- name
1823+
type: object
1824+
type: array
1825+
x-kubernetes-list-map-keys:
1826+
- name
1827+
x-kubernetes-list-type: map
17451828
type: object
17461829
type: object
17471830
served: true

config/crd/bases/operator.kcp.io_shards.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,74 @@ spec:
120120
type: string
121121
type: object
122122
type: object
123+
auth:
124+
description: 'Optional: Auth configures various aspects of Authentication
125+
and Authorization for this shard.'
126+
properties:
127+
dropGroups:
128+
description: |-
129+
Optional: DropGroups configures groups to be dropped before forwarding requests to Shards.
130+
If set, it will mount every shard's service account certificate to the front-proxy.
131+
items:
132+
type: string
133+
type: array
134+
oidc:
135+
description: 'Optional: OIDC configures OpenID Connect Authentication.'
136+
properties:
137+
clientID:
138+
description: ClientID is the OIDC client ID configured on
139+
the issuer side for this KCP instance.
140+
type: string
141+
clientSecret:
142+
description: |-
143+
Optionally provide the client secret for the OIDC client. This is not used by KCP itself, but is used to generate
144+
a OIDC kubeconfig that can be shared with users to log in via the OIDC provider.
145+
type: string
146+
groupsClaim:
147+
description: 'Experimental: Optionally provides a custom claim
148+
for fetching groups. The claim must be a string or an array
149+
of strings.'
150+
type: string
151+
groupsPrefix:
152+
description: |-
153+
Optionally sets a custom groups prefix. This defaults to "oidc:" if unset, which means a group called "group1"
154+
on the OIDC side will be recognised as "oidc:group1" in KCP.
155+
type: string
156+
issuerURL:
157+
description: IssuerURL is used for the OIDC issuer URL. Only
158+
https URLs will be accepted.
159+
type: string
160+
usernameClaim:
161+
description: Optionally uses a custom claim for fetching the
162+
username. This defaults to "sub" if unset.
163+
type: string
164+
usernamePrefix:
165+
description: |-
166+
Optionally sets a custom username prefix. This defaults to "oidc:" if unset, which means a user called "[email protected]"
167+
on the OIDC side will be recognised as "oidc:[email protected]" in KCP.
168+
type: string
169+
required:
170+
- clientID
171+
- issuerURL
172+
type: object
173+
passOnGroups:
174+
description: 'Optional: PassOnGroups configures groups to be passed
175+
on before forwarding requests to Shards'
176+
items:
177+
type: string
178+
type: array
179+
serviceAccount:
180+
description: 'Optional: serviceAccountAuthentication configures
181+
ServiceAccount Authentication.'
182+
properties:
183+
enabled:
184+
description: 'Optional: Enabled enables or disables ServiceAccount
185+
Authentication.'
186+
type: boolean
187+
required:
188+
- enabled
189+
type: object
190+
type: object
123191
authorization:
124192
properties:
125193
webhook:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: operator.kcp.io/v1alpha1
2+
kind: Kubeconfig
3+
metadata:
4+
labels:
5+
app.kubernetes.io/name: kcp-operator
6+
app.kubernetes.io/managed-by: kustomize
7+
name: kubeconfig-admin-sample
8+
spec:
9+
10+
groups:
11+
- kcp-admin
12+
validity: 8766h
13+
secretRef:
14+
name: sample-admin-kubeconfig
15+
target:
16+
frontProxyRef:
17+
name: frontproxy-sample

internal/controller/rootshard_controller.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controller
1919
import (
2020
"context"
2121
"fmt"
22+
"sort"
2223

2324
certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
2425
k8creconciling "k8c.io/reconciler/pkg/reconciling"
@@ -31,9 +32,12 @@ import (
3132
"k8s.io/apimachinery/pkg/runtime"
3233
"k8s.io/apimachinery/pkg/types"
3334
kerrors "k8s.io/apimachinery/pkg/util/errors"
35+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3436
ctrl "sigs.k8s.io/controller-runtime"
3537
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
38+
"sigs.k8s.io/controller-runtime/pkg/handler"
3639
"sigs.k8s.io/controller-runtime/pkg/log"
40+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3741

3842
"github.com/kcp-dev/kcp-operator/internal/reconciling"
3943
"github.com/kcp-dev/kcp-operator/internal/resources"
@@ -49,13 +53,29 @@ type RootShardReconciler struct {
4953

5054
// SetupWithManager sets up the controller with the Manager.
5155
func (r *RootShardReconciler) SetupWithManager(mgr ctrl.Manager) error {
56+
shardHandler := handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, obj ctrlruntimeclient.Object) []reconcile.Request {
57+
shard := obj.(*operatorv1alpha1.Shard)
58+
59+
var rootShard operatorv1alpha1.RootShard
60+
if err := mgr.GetClient().Get(ctx, ctrlruntimeclient.ObjectKey{Namespace: shard.Namespace, Name: shard.Spec.RootShard.Reference.Name}, &rootShard); err != nil {
61+
utilruntime.HandleError(err)
62+
return nil
63+
}
64+
65+
var requests []reconcile.Request
66+
requests = append(requests, reconcile.Request{NamespacedName: ctrlruntimeclient.ObjectKeyFromObject(&rootShard)})
67+
68+
return requests
69+
})
70+
5271
return ctrl.NewControllerManagedBy(mgr).
5372
For(&operatorv1alpha1.RootShard{}).
5473
Owns(&appsv1.Deployment{}).
5574
Owns(&corev1.ConfigMap{}).
5675
Owns(&corev1.Service{}).
5776
Owns(&corev1.Secret{}).
5877
Owns(&certmanagerv1.Certificate{}).
78+
Watches(&operatorv1alpha1.Shard{}, shardHandler).
5979
Complete(r)
6080
}
6181

@@ -197,6 +217,20 @@ func (r *RootShardReconciler) reconcileStatus(ctx context.Context, oldRootShard
197217
rootShard.Status.Phase = operatorv1alpha1.RootShardPhaseProvisioning
198218
}
199219

220+
shards, err := getRootShardChildren(ctx, r.Client, rootShard)
221+
if err != nil {
222+
errs = append(errs, err)
223+
} else {
224+
rootShard.Status.Shards = make([]operatorv1alpha1.ShardReference, len(shards))
225+
for i, shard := range shards {
226+
rootShard.Status.Shards[i] = operatorv1alpha1.ShardReference{Name: shard.Name}
227+
}
228+
}
229+
// sort the shards by name for equality comparison
230+
sort.Slice(rootShard.Status.Shards, func(i, j int) bool {
231+
return rootShard.Status.Shards[i].Name < rootShard.Status.Shards[j].Name
232+
})
233+
200234
// only patch the status if there are actual changes.
201235
if !equality.Semantic.DeepEqual(oldRootShard.Status, rootShard.Status) {
202236
if err := r.Status().Patch(ctx, rootShard, ctrlruntimeclient.MergeFrom(oldRootShard)); err != nil {

internal/controller/util.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
apimeta "k8s.io/apimachinery/pkg/api/meta"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2727
"k8s.io/apimachinery/pkg/types"
28+
kerrors "k8s.io/apimachinery/pkg/util/errors"
2829
"k8s.io/utils/ptr"
2930
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
3031

@@ -117,3 +118,25 @@ func fetchRootShard(ctx context.Context, client ctrlruntimeclient.Client, namesp
117118
Message: "RootShard reference is valid.",
118119
}, rootShard
119120
}
121+
122+
// getRootShardChildren returns all shards that are currently registered with the given root shard.
123+
func getRootShardChildren(ctx context.Context, client ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard) ([]operatorv1alpha1.Shard, error) {
124+
var errs []error
125+
126+
var shards operatorv1alpha1.ShardList
127+
err := client.List(ctx, &shards, &ctrlruntimeclient.ListOptions{
128+
Namespace: rootShard.Namespace,
129+
})
130+
if err != nil {
131+
errs = append(errs, err)
132+
}
133+
134+
var result []operatorv1alpha1.Shard
135+
for _, shard := range shards.Items {
136+
if shard.Spec.RootShard.Reference.Name == rootShard.Name {
137+
result = append(result, shard)
138+
}
139+
}
140+
141+
return result, kerrors.NewAggregate(errs)
142+
}

0 commit comments

Comments
 (0)