feat(crossplane): try to organise crossplane crds

also add a keycloak-oidc-client composition
This commit is contained in:
Vegard Hagen
2025-01-03 14:55:12 +01:00
parent 253e65a708
commit bff741753a
18 changed files with 345 additions and 28 deletions
@@ -6,6 +6,8 @@ spec:
providerConfigName: default
providerSecretName: keycloak-credentials
realm: homelab
builtinAuthenticationFlows:
- browser
builtinClients:
- account
- account-console
@@ -0,0 +1,189 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: keycloak-oidc-client
spec:
compositeTypeRef:
apiVersion: oidc.homelab.olav.ninja/v1alpha1
kind: XOidcClient
mode: Pipeline
pipeline:
- step: create-client
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: Client
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: {{ .observed.composite.resource.metadata.name }}
spec:
forProvider:
name: {{ .observed.composite.resource.spec.displayName }}
accessType: {{ .observed.composite.resource.spec.type }}
clientId: {{ .observed.composite.resource.spec.clientId }}
{{ with .observed.composite.resource.spec.clientSecretSecretRef }}
clientSecretSecretRef: {{ toYaml . | nindent 6 }}
{{ end }}
description: {{ .observed.composite.resource.spec.description }}
{{ with .observed.composite.resource.spec.baseUrl }}
baseUrl: {{ . }}
{{ end }}
{{ with .observed.composite.resource.spec.redirectUris }}
validRedirectUris: {{ toYaml . | nindent 6 }}
{{ end }}
{{ with .observed.composite.resource.spec.postLogoutRedirectUris }}
validPostLogoutRedirectUris: {{ toYaml . | nindent 6 }}
{{ end }}
{{ with .observed.composite.resource.spec.webOrigins }}
webOrigins: {{ toYaml . | nindent 6 }}
{{ end }}
{{ if has "client_credentials" .observed.composite.resource.spec.grantTypes }}
serviceAccountsEnabled: true
{{ end }}
{{ if has "code" .observed.composite.resource.spec.grantTypes }}
standardFlowEnabled: true
{{ end }}
{{ if has "device_code" .observed.composite.resource.spec.grantTypes }}
oauth2DeviceAuthorizationGrantEnabled: true
{{ end }}
{{ if has "password" .observed.composite.resource.spec.grantTypes }}
directAccessGrantsEnabled: true
{{- end }}
realmIdRef:
name: {{ .observed.composite.resource.spec.realm }}
- step: create-audience-mapper
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
{{ if ne $.observed.resources nil }}
apiVersion: client.keycloak.crossplane.io/v1alpha1
kind: ProtocolMapper
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: {{ .observed.composite.resource.metadata.name }}-audience-mapper
spec:
forProvider:
name: Set token audience
protocol: openid-connect
protocolMapper: oidc-audience-mapper
config:
included.client.audience: "{{ .observed.composite.resource.spec.clientId }}"
id.token.claim: "false"
access.token.claim: "true"
introspection.token.claim: "true"
userinfo.token.claim: "false"
clientId: {{ ( index .observed.resources .observed.composite.resource.metadata.name ).resource.status.atProvider.id | default "null" }}
realmIdRef:
name: {{ .observed.composite.resource.spec.realm }}
{{ end }}
- step: create-usermodel-property-mapper
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
{{ if ne $.observed.resources nil }}
apiVersion: client.keycloak.crossplane.io/v1alpha1
kind: ProtocolMapper
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: {{ .observed.composite.resource.metadata.name }}-sub-mapper
spec:
forProvider:
name: Username as sub claim
protocol: openid-connect
protocolMapper: oidc-usermodel-property-mapper
config:
user.attribute: username
id.token.claim: "true"
access.token.claim: "true"
claim.name: sub
userinfo.token.claim: "true"
clientId: {{ ( index .observed.resources .observed.composite.resource.metadata.name ).resource.status.atProvider.id | default "null" }}
realmIdRef:
name: {{ .observed.composite.resource.spec.realm }}
{{ end }}
- step: create-client-default-scopes
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
{{ if ne $.observed.resources nil }}
{{ if .observed.composite.resource.spec.defaultScopes }}
apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: ClientDefaultScopes
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: {{ .observed.composite.resource.metadata.name }}-default-scopes
spec:
forProvider:
{{ with .observed.composite.resource.spec.defaultScopes }}
defaultScopes: {{ toYaml . | nindent 6 }}
{{ end }}
clientId: {{ ( index .observed.resources .observed.composite.resource.metadata.name ).resource.status.atProvider.id | default "null" }}
realmIdRef:
name: {{ .observed.composite.resource.spec.realm }}
{{ end }}
{{ end }}
- step: create-service-account-role
functionRef:
name: function-go-templating
input:
apiVersion: gotemplating.fn.crossplane.io/v1beta1
kind: GoTemplate
source: Inline
inline:
template: |
{{ range .observed.composite.resource.spec.serviceAccountRoles }}
---
apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1
kind: ExtraResources
requirements:
client:
apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: Client
matchName: {{ .client }}
realm:
apiVersion: realm.keycloak.crossplane.io/v1alpha1
kind: Realm
matchName: {{ .realm }}
{{ end }}
{{ if and (ne .observed.resources nil) (ne .extraResources nil) }}
{{ range $i, $serviceAccountRole := .observed.composite.resource.spec.serviceAccountRoles }}
{{ $client := (index (index $.extraResources "client").items $i).resource }}
{{ $realm := (index (index $.extraResources "realm").items $i).resource }}
---
apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: ClientServiceAccountRole
metadata:
annotations:
gotemplating.fn.crossplane.io/composition-resource-name: {{ $.observed.composite.resource.metadata.name }}-{{ .role }}
spec:
forProvider:
clientId: {{ $client.status.atProvider.id }}
realmId: {{ $realm.status.atProvider.id }}
role: {{ $serviceAccountRole.role }}
serviceAccountUserId: {{ ( index $.observed.resources $.observed.composite.resource.metadata.name ).resource.status.atProvider.serviceAccountUserId | default "null" }}
{{ end }}
{{ end }}
- step: automatically-detect-ready-composed-resources
functionRef:
name: function-auto-ready
@@ -2,5 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- keycloak-credentials.yaml
- config.yaml
- keycloak-builtin-objects.yaml
- keycloak-oidc-client.yaml
@@ -1,21 +0,0 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-extra-resources
spec:
package: xpkg.upbound.io/crossplane-contrib/function-extra-resources:v0.0.3 # renovate: github-releases=crossplane-contrib/function-extra-resources
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-auto-ready
spec:
package: xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.4.0 # renovate: github-releases=crossplane-contrib/function-auto-ready
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-keycloak-builtin-objects
spec:
package: registry.gitlab.com/corewire/images/crossplane/function-keycloak-builtin-objects:v3.0.0 # renovate: gitlab-releases=corewire/images/crossplane/function-keycloak-builtin-objects
packagePullPolicy: Always
@@ -2,7 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- provider.yaml
- functions.yaml
- composition.yaml
- xrd.yaml
- provider-config.yaml
- keycloak-credentials.yaml
@@ -0,0 +1,6 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-auto-ready
spec:
package: xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.4.0 # renovate: github-releases=crossplane-contrib/function-auto-ready
@@ -0,0 +1,6 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-extra-resources
spec:
package: xpkg.upbound.io/crossplane-contrib/function-extra-resources:v0.0.3 # renovate: github-releases=crossplane-contrib/function-extra-resources
@@ -0,0 +1,6 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-go-templating
spec:
package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.9.0 # renovate: github-releases=crossplane-contrib/function-go-templating
@@ -0,0 +1,7 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-keycloak-builtin-objects
spec:
package: registry.gitlab.com/corewire/images/crossplane/function-keycloak-builtin-objects:v3.0.0 # renovate: gitlab-releases=corewire/images/crossplane/function-keycloak-builtin-objects
packagePullPolicy: Always
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- auto-ready.yaml
- extra-resources.yaml
- go-templating.yaml
- keycloak-builtin-objects.yaml
@@ -2,4 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- keycloak
- keycloak.yaml
@@ -0,0 +1,110 @@
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xoidcclients.oidc.homelab.olav.ninja
spec:
group: oidc.homelab.olav.ninja
names:
kind: XOidcClient
plural: xoidcclients
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
baseUrl:
description: Default URL to use when the auth server needs to
redirect or link back to the client.
type: string
displayName:
type: string
description: The display name of this client in the GUI
clientId:
type: string
description: The Client ID for this client, referenced in the URI during authentication and in issued tokens
clientSecretSecretRef:
description: |-
The client or client secret registered within the identity provider. This field is able to obtain its value from vault, use $${vault.ID} format.
Client Secret.
properties:
key:
description: The key to select.
type: string
name:
description: Name of the secret.
type: string
namespace:
description: Namespace of the secret.
type: string
required:
- key
- name
- namespace
type: object
description:
type: string
description: The description of this client in the GUI
defaultScopes:
type: array
items:
type: string
description: |-
The default scopes to be requested when asking for authorization
grantTypes:
type: array
items:
type: string
description: |-
A list of grant types that should be enabled for the client
postLogoutRedirectUris:
type: array
items:
type: string
description: A list of valid URIs a browser is permitted to redirect
to after a successful logout.
realm:
type: string
description: The realm this client is attached to
redirectUris:
type: array
items:
type: string
description: |-
A list of valid URIs a browser is permitted to redirect to after a successful login or logout. Simple
wildcards in the form of an asterisk can be used here. This attribute must be set if either standard_flow_enabled or implicit_flow_enabled
is set to true.
serviceAccountRoles:
type: array
items:
type: object
properties:
client:
type: string
realm:
type: string
role:
type: string
description: |-
A list of roles to assign to the clients service account
type:
type: string
description: Specifies the type of client
webOrigins:
type: array
items:
type: string
description: |-
A list of allowed CORS origins. To permit all valid
redirect URIs, add +. Note that this will not include the *
wildcard. To permit all origins, explicitly add *.
required:
- clientId
- realm
required:
- spec
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- keycloak-builtin-objects.yaml
- keycloak-oidc-client.yaml