Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ds/getting started #191

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 154 additions & 0 deletions docs/getting-started/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Getting started

This guide will let you set up everything to try out crossplane-contrib/provider-keycloak on a fresh kind cluster.

## Prerequisites

[ctlptl](https://github.com/tilt-dev/ctlptl), [kind](https://kind.sigs.k8s.io/), [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl), [helm](https://helm.sh/docs/intro/install/)

This example is written with linux in mind, but it will work on Windows with PowerShell 7 as well.

## Keycloak up and running

This is an express installation of keycloak on new kind cluster.

``` sh
ctlptl apply -f kind-kustomize/cluster/cluster.yaml

kubectl apply -f kind-kustomize/keycloak/keycloak.yaml
kubectl wait --for=condition=Available deployment kc -n keycloak --timeout=180s
kubectl port-forward -n keycloak svc/keycloak 8080:80
```


```
❯ ctlptl apply -f kind-kustomize/cluster/cluster.yaml
No kind clusters found.
Creating cluster "provider-keycloak-cluster" ...
✓ Ensuring node image (kindest/node:v1.31.0) 🖼
✓ Preparing nodes 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to "kind-provider-keycloak-cluster"
You can now use your cluster with:

kubectl cluster-info --context kind-provider-keycloak-cluster

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
Switched to context "kind-provider-keycloak-cluster".
🔌 Connected cluster kind-provider-keycloak-cluster to registry ctlptl-registry at localhost:52145
👐 Push images to the cluster like 'docker push localhost:52145/alpine'
cluster.ctlptl.dev/kind-provider-keycloak-cluster created

```

```
> kubectl apply -f kind-kustomize/keycloak/keycloak.yaml
namespace/keycloak created
configmap/keycloak-cm created
service/keycloak created
deployment.apps/kc created

```

```
> kubectl wait --for=condition=Available deployment kc -n keycloak --timeout=180s
deployment.apps/kc condition met

> kubectl port-forward -n keycloak svc/keycloak 8080:80
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
```

When surfing into the keycloak UI at http://localhost:8080 you can logon as admin/admin. You are then prompted to replace the temporary admin account with a permanent one. For the purpose of demonstrating or getting started with this crossplane provider you can skip this step. Make sure the new user can log on and has the correct access (typically the admin role) before deleting the temporary user.

![An orange banner at the top urging the temporary user to be replaced](assets/replace-user-banner.png)

Refer to the keycloak documentation on how to best harden security for your setup of keycloak and consider using an external database. https://www.keycloak.org/docs/latest/server_admin/#proc-creating-user_server_administration_guide


## Installing crossplane

The procedure to install crossplane is described in better detail on the crossplane main repository, and on their webpage: https://docs.crossplane.io/latest/software/install/

Here is a minimal example to get up and running with everything you need.

``` sh
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane --namespace crossplane-system --create-namespace crossplane-stable/crossplane

```

The following step will bootstrap a working client in the master realm with the admin role that crossplane will use in a future step. You should consider learning to set up a similar client through the UI or through the API in a manner which fits your security practices.

``` sh
# creates a config map with the script to run
kubectl create configmap client-script -n keycloak --from-file=kind-kustomize/crossplane/create-client.sh

# creates a job to run the script from within kubernetes.
kubectl apply -f kind-kustomize/crossplane/create-client.yaml

```

We can now create the keycloak crossplane provider and configure it to use the client withing the master realm to perform actions there.

The settings for the client will also make it appear as a service-account user in the realm.

![Displays the crossplane service-account user](assets/service-account-crossplane.png)

``` sh
# deploys the keycloak provider
kubectl apply -f ./kind-kustomize/crossplane/provider.yaml
echo "Waiting for the required CRDs to show up..."
sleep 10
kubectl wait --for=condition=established crd providerconfigs.keycloak.crossplane.io --timeout=15s

kubectl apply -f ./kind-kustomize/crossplane/providerconfig.yaml
```

Finally we can try out using our keycloak crossplane provider, here is an example of creating a new realm.

``` sh
kubectl apply -f ./kind-kustomize/test-realm/realm.yaml
```

If we want to observe the new realm to be able to use data generated inside it we can leverage existing functions for the provider.

```
kubectl apply -f ./kind-kustomize/crossplane/keycloak-built-in-objects/xrd.yaml
kubectl apply -f ./kind-kustomize/crossplane/keycloak-built-in-objects/composition.yaml
kubectl apply -f ./kind-kustomize/crossplane/keycloak-built-in-objects/functions.yaml
# written specifically for the test-realm
kubectl apply -f ./kind-kustomize/crossplane/keycloak-built-in-objects/xr-test-realm.yaml
```

Once synced the observable default items will all be available through kube-api.

``` sh
kubectl get roles.role.keycloak.crossplane.io
```

This will then allow us to reference them in crossplane like in the example below that creates a user in the new role and assigns them the administrative role. The format is `builtin-<realm-name>-<client-name>-<client-role-name>`. Thus for role *realm-admin* in the realm *test-realm* which is a client role for the client *realm-management* the name would be `builtin-test-realm-realm-management-realm-admin` :)

``` sh
kubectl apply -f ./kind-kustomize/test-realm/admin-user.yaml
```

Once this has synched, you can surf to the security admin console of the test-realm, sign in with testadmin/testadmin and you will be prompted to update your temporary password for the user testadmin.

http://localhost:8080/admin/test-realm/console/

![update-temporary-password](assets/update-temporary-password.png)

## clean up

To delete the cluster you created with ctlptl, run the following.

``` sh
ctlptl delete -f kind-kustomize/cluster/cluster.yaml
```
9 changes: 9 additions & 0 deletions docs/getting-started/kind-kustomize/cluster/cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: ctlptl.dev/v1alpha1
kind: Cluster
product: kind
registry: ctlptl-registry
kindV1Alpha4Cluster:
name: provider-keycloak-cluster
nodes:
- role: worker
- role: control-plane
45 changes: 45 additions & 0 deletions docs/getting-started/kind-kustomize/crossplane/create-client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# uses curl to invoke the keycloak REST api for crossplane
# gets a token for the master realm
echo "Logging on as admin in keycloak to create the crossplane client and grant it the admin role in the master realm"
mastertoken=$(curl -k -g -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" -d "client_secret=" "http://keycloak.keycloak:80/realms/master/protocol/openid-connect/token" | sed 's/.*access_token":"//g' | sed 's/".*//g');
# echo $mastertoken;

id="9d2308c3-8972-40cf-9cca-1256745c16d4";
url="http://keycloak.keycloak:80/admin/realms/master";
clienturl="$url/clients/$id";

# creates a new client named "crossplane"
curl -X POST -k -g "$url/clients" \
-H "Authorization: Bearer $mastertoken" \
-H "Content-Type: application/json" \
--data-raw '
{
"id":"'$id'",
"name":"crossplane",
"clientId":"crossplane",
"secret":"xppw_OJKzQjuBoyPlIEePgiWg",
"clientAuthenticatorType":"client-secret",
"serviceAccountsEnabled":"true",
"standardFlowEnabled":"false"
}'

# GETs the service-account-user for the client - GET $url/clients/{id}/service-account-user
userid=$(curl -X GET -k -g "$clienturl/service-account-user" -H "Authorization: Bearer $mastertoken" | sed 's/.*id":"//g' | sed 's/".*//g')

# lists available realm roles
# GET /{realm}/users/{id}/role-mappings/realm/available
roles=$(curl -X GET -k -g -H "Authorization: Bearer $mastertoken" "$url/roles")

# gets the id of the admin role
admin_id=$(echo $roles | jq -r '.[] | select(.name == "admin") | .id')

# adds service account role admin to the client's user
curl -X POST -k -g "$url/users/$userid/role-mappings/realm/" \
-H "Authorization: Bearer $mastertoken" \
-H "Content-Type: application/json" \
--data-raw '[
{
"id":"'$admin_id'",
"name":"admin"
}
]'
59 changes: 59 additions & 0 deletions docs/getting-started/kind-kustomize/crossplane/create-client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# The pod for this job should mount a volume that will be created from a config map containing a file with a bash script that it runs
apiVersion: batch/v1
kind: Job
metadata:
name: create-client-crossplane
namespace: keycloak
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
initContainers:
- command:
- sh
- -c
- |
set -x;
echo "Waiting for master realm to become ready..."
while [ $(curl -sw '%{http_code}' "http://keycloak.keycloak/realms/master" -o /dev/null) -ne 200 ]; do
sleep 15;
done;

echo "$SVC_HOST:$SVC_PORT connection OK ✓"
image: dwdraju/alpine-curl-jq
imagePullPolicy: IfNotPresent
name: svcchecker
resources:
limits:
cpu: 20m
memory: 32Mi
requests:
cpu: 20m
memory: 32Mi
securityContext:
allowPrivilegeEscalation: false
runAsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
containers:
- name: bash
image: dwdraju/alpine-curl-jq
command: ["bash"]
args: ["/opt/script/create-client.sh"]
volumeMounts:
- name: client-script
mountPath: /opt/script/
resources:
limits:
cpu: 100m
memory: 32Mi
requests:
cpu: 100m
memory: 32Mi
volumes:
- name: client-script
configMap:
name: client-script
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: keycloak-builtin-objects
spec:
compositeTypeRef:
apiVersion: keycloak.crossplane.io/v1alpha1
kind: XBuiltinObjects
mode: Pipeline
pipeline:
- step: pull-provider-configs
functionRef:
name: function-extra-resources
input:
apiVersion: extra-resources.fn.crossplane.io/v1beta1
kind: Input
spec:
extraResources:
- kind: Secret
into: secrets
apiVersion: v1
type: Selector
selector:
minMatch: 1
maxMatch: 100
matchLabels:
- key: type
type: Value
value: provider-credentials
- kind: Role
into: roles
apiVersion: role.keycloak.crossplane.io/v1alpha1
type: Selector
selector:
minMatch: 0
maxMatch: 20
matchLabels:
- key: type
type: Value
value: modifiedrole
- step: keycloak-builtin-objects
functionRef:
name: function-keycloak-builtin-objects
- step: automatically-detect-ready-composed-resources
functionRef:
name: function-auto-ready
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-extra-resources
annotations:
# This tells crossplane beta render to connect to the function locally.
#render.crossplane.io/runtime: Development
spec:
# This is ignored when using the Development runtime.
package: xpkg.upbound.io/crossplane-contrib/function-extra-resources:v0.0.3
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-auto-ready
annotations:
# This tells crossplane beta render to connect to the function locally.
#render.crossplane.io/runtime: Development
spec:
# This is ignored when using the Development runtime.
package: xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.2.1
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-keycloak-builtin-objects
#annotations:
# # This tells crossplane beta render to connect to the function locally.
# render.crossplane.io/runtime: Development
spec:
# This is ignored when using the Development runtime.
package: registry.gitlab.com/corewire/images/crossplane/function-keycloak-builtin-objects:v1.0.0
packagePullPolicy: Always
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
# Example for a custom realm (custom realms have different builtin clients/roles than the master realm)
apiVersion: keycloak.crossplane.io/v1alpha1
kind: XBuiltinObjects
metadata:
name: keycloak-builtin-objects-dev
spec:
providerConfigName: keycloak-config
providerSecretName: keycloak-credentials
realm: test-realm
builtinClients:
- account
- account-console
- admin-cli
- broker
- realm-management
- security-admin-console
builtinRealmRoles:
- offline_access
- uma_authorization
Loading