forked from DevFW-CICD/stacks
472 lines
18 KiB
YAML
472 lines
18 KiB
YAML
# resources here are used to configure keycloak instance for SSO
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: keycloak-config
|
|
namespace: keycloak
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata:
|
|
name: keycloak-config
|
|
namespace: keycloak
|
|
rules:
|
|
- apiGroups: [""]
|
|
resources: ["secrets"]
|
|
verbs: ["get", "create", "update", "patch"]
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: RoleBinding
|
|
metadata:
|
|
name: keycloak-config
|
|
namespace: keycloak
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: keycloak-config
|
|
namespace: keycloak
|
|
roleRef:
|
|
kind: Role
|
|
name: keycloak-config
|
|
apiGroup: rbac.authorization.k8s.io
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata:
|
|
name: keycloak-config
|
|
namespace: argocd
|
|
rules:
|
|
- apiGroups: [""]
|
|
resources: ["secrets"]
|
|
verbs: ["get"]
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: RoleBinding
|
|
metadata:
|
|
name: keycloak-config
|
|
namespace: argocd
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: keycloak-config
|
|
namespace: keycloak
|
|
roleRef:
|
|
kind: Role
|
|
name: keycloak-config
|
|
apiGroup: rbac.authorization.k8s.io
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: config-job
|
|
namespace: keycloak
|
|
data:
|
|
client-scope-groups-payload.json: |
|
|
{
|
|
"name": "groups",
|
|
"description": "groups a user belongs to",
|
|
"attributes": {
|
|
"consent.screen.text": "Access to groups a user belongs to.",
|
|
"display.on.consent.screen": "true",
|
|
"include.in.token.scope": "true",
|
|
"gui.order": ""
|
|
},
|
|
"type": "default",
|
|
"protocol": "openid-connect"
|
|
}
|
|
group-admin-payload.json: |
|
|
{"name":"admin"}
|
|
group-base-user-payload.json: |
|
|
{"name":"base-user"}
|
|
group-mapper-payload.json: |
|
|
{
|
|
"protocol": "openid-connect",
|
|
"protocolMapper": "oidc-group-membership-mapper",
|
|
"name": "groups",
|
|
"config": {
|
|
"claim.name": "groups",
|
|
"full.path": "false",
|
|
"id.token.claim": "true",
|
|
"access.token.claim": "true",
|
|
"userinfo.token.claim": "true"
|
|
}
|
|
}
|
|
realm-payload.json: |
|
|
{"realm":"cnoe","enabled":true}
|
|
user-password.json: |
|
|
{
|
|
"temporary": false,
|
|
"type": "password",
|
|
"value": "${USER1_PASSWORD}"
|
|
}
|
|
user-user1.json: |
|
|
{
|
|
"username": "user1",
|
|
"email": "",
|
|
"firstName": "user",
|
|
"lastName": "one",
|
|
"requiredActions": [],
|
|
"emailVerified": false,
|
|
"groups": [
|
|
"/admin"
|
|
],
|
|
"enabled": true
|
|
}
|
|
user-user2.json: |
|
|
{
|
|
"username": "user2",
|
|
"email": "",
|
|
"firstName": "user",
|
|
"lastName": "two",
|
|
"requiredActions": [],
|
|
"emailVerified": false,
|
|
"groups": [
|
|
"/base-user"
|
|
],
|
|
"enabled": true
|
|
}
|
|
argo-client-payload.json: |
|
|
{
|
|
"protocol": "openid-connect",
|
|
"clientId": "argo-workflows",
|
|
"name": "Argo Workflows Client",
|
|
"description": "Used for Argo Workflows SSO",
|
|
"publicClient": false,
|
|
"authorizationServicesEnabled": false,
|
|
"serviceAccountsEnabled": false,
|
|
"implicitFlowEnabled": false,
|
|
"directAccessGrantsEnabled": true,
|
|
"standardFlowEnabled": true,
|
|
"frontchannelLogout": true,
|
|
"attributes": {
|
|
"saml_idp_initiated_sso_url_name": "",
|
|
"oauth2.device.authorization.grant.enabled": false,
|
|
"oidc.ciba.grant.enabled": false
|
|
},
|
|
"alwaysDisplayInConsole": false,
|
|
"rootUrl": "",
|
|
"baseUrl": "",
|
|
"redirectUris": [
|
|
"https://{{{ .Env.DOMAIN }}}:443/argo-workflows/oauth2/callback"
|
|
],
|
|
"webOrigins": [
|
|
"/*"
|
|
]
|
|
}
|
|
|
|
backstage-client-payload.json: |
|
|
{
|
|
"protocol": "openid-connect",
|
|
"clientId": "backstage",
|
|
"name": "Backstage Client",
|
|
"description": "Used for Backstage SSO",
|
|
"publicClient": false,
|
|
"authorizationServicesEnabled": false,
|
|
"serviceAccountsEnabled": false,
|
|
"implicitFlowEnabled": false,
|
|
"directAccessGrantsEnabled": true,
|
|
"standardFlowEnabled": true,
|
|
"frontchannelLogout": true,
|
|
"attributes": {
|
|
"saml_idp_initiated_sso_url_name": "",
|
|
"oauth2.device.authorization.grant.enabled": false,
|
|
"oidc.ciba.grant.enabled": false
|
|
},
|
|
"alwaysDisplayInConsole": false,
|
|
"rootUrl": "",
|
|
"baseUrl": "",
|
|
"redirectUris": [
|
|
"https://{{{ .Env.DOMAIN }}}:443/api/auth/keycloak-oidc/handler/frame"
|
|
],
|
|
"webOrigins": [
|
|
"/*"
|
|
]
|
|
}
|
|
|
|
grafana-client-payload.json: |
|
|
{
|
|
"clientId": "grafana",
|
|
"name": "Grafana Client",
|
|
"description": "Used for Grafana SSO",
|
|
"rootUrl": "https://{{{ .Env.DOMAIN }}}/grafana",
|
|
"adminUrl": "https://{{{ .Env.DOMAIN }}}/grafana",
|
|
"baseUrl": "https://{{{ .Env.DOMAIN }}}/grafana",
|
|
"surrogateAuthRequired": false,
|
|
"enabled": true,
|
|
"alwaysDisplayInConsole": false,
|
|
"clientAuthenticatorType": "client-secret",
|
|
"secret": "aQ1UV9Z6ZuLBwrgw8vV9ijf6LA95yMZL",
|
|
"redirectUris": [
|
|
"http://{{{ .Env.DOMAIN }}}/grafana/*"
|
|
],
|
|
"webOrigins": [
|
|
"https://{{{ .Env.DOMAIN }}}/grafana"
|
|
],
|
|
"notBefore": 0,
|
|
"bearerOnly": false,
|
|
"consentRequired": false,
|
|
"standardFlowEnabled": true,
|
|
"implicitFlowEnabled": false,
|
|
"directAccessGrantsEnabled": true,
|
|
"serviceAccountsEnabled": false,
|
|
"publicClient": false,
|
|
"frontchannelLogout": true,
|
|
"protocol": "openid-connect",
|
|
"attributes": {
|
|
"oidc.ciba.grant.enabled": "false",
|
|
"backchannel.logout.session.required": "true",
|
|
"display.on.consent.screen": "false",
|
|
"oauth2.device.authorization.grant.enabled": "false",
|
|
"backchannel.logout.revoke.offline.tokens": "false"
|
|
},
|
|
"authenticationFlowBindingOverrides": {},
|
|
"fullScopeAllowed": true,
|
|
"nodeReRegistrationTimeout": -1,
|
|
"protocolMappers": [
|
|
{
|
|
"name": "client roles",
|
|
"protocol": "openid-connect",
|
|
"protocolMapper": "oidc-usermodel-client-role-mapper",
|
|
"consentRequired": false,
|
|
"config": {
|
|
"multivalued": "true",
|
|
"userinfo.token.claim": "false",
|
|
"user.attribute": "foo",
|
|
"id.token.claim": "true",
|
|
"access.token.claim": "true",
|
|
"claim.name": "resource_access.${client_id}.roles",
|
|
"jsonType.label": "String"
|
|
}
|
|
}
|
|
],
|
|
"defaultClientScopes": [
|
|
"web-origins",
|
|
"acr",
|
|
"roles",
|
|
"offline_access",
|
|
"profile",
|
|
"email"
|
|
],
|
|
"optionalClientScopes": [
|
|
"address",
|
|
"phone",
|
|
"microprofile-jwt"
|
|
],
|
|
"access": {
|
|
"view": true,
|
|
"configure": true,
|
|
"manage": true
|
|
}
|
|
}
|
|
|
|
---
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: config
|
|
namespace: keycloak
|
|
annotations:
|
|
argocd.argoproj.io/hook: PostSync
|
|
spec:
|
|
template:
|
|
metadata:
|
|
generateName: config
|
|
spec:
|
|
serviceAccountName: keycloak-config
|
|
restartPolicy: Never
|
|
volumes:
|
|
- name: keycloak-config
|
|
secret:
|
|
secretName: keycloak-config
|
|
- name: config-payloads
|
|
configMap:
|
|
name: config-job
|
|
containers:
|
|
- name: kubectl
|
|
image: docker.io/library/ubuntu:22.04
|
|
volumeMounts:
|
|
- name: keycloak-config
|
|
readOnly: true
|
|
mountPath: "/var/secrets/"
|
|
- name: config-payloads
|
|
readOnly: true
|
|
mountPath: "/var/config/"
|
|
command: ["/bin/bash", "-c"]
|
|
args:
|
|
- |
|
|
#! /bin/bash
|
|
|
|
set -ex -o pipefail
|
|
|
|
apt -qq update && apt -qq install curl jq -y
|
|
|
|
ADMIN_PASSWORD=$(cat /var/secrets/KEYCLOAK_ADMIN_PASSWORD)
|
|
USER1_PASSWORD=$(cat /var/secrets/USER_PASSWORD)
|
|
|
|
{{{ if eq .Env.CLUSTER_TYPE "kind" }}}
|
|
KEYCLOAK_URL=http://keycloak.keycloak.svc.cluster.local:8080/keycloak
|
|
{{{ end }}}
|
|
{{{ if eq .Env.CLUSTER_TYPE "osc" }}}
|
|
KEYCLOAK_URL=https://{{{ .Env.DOMAIN }}}/keycloak
|
|
{{{ end }}}
|
|
|
|
KEYCLOAK_TOKEN=$(curl -sS --fail-with-body -X POST -H "Content-Type: application/x-www-form-urlencoded" \
|
|
--data-urlencode "username=cnoe-admin" \
|
|
--data-urlencode "password=${ADMIN_PASSWORD}" \
|
|
--data-urlencode "grant_type=password" \
|
|
--data-urlencode "client_id=admin-cli" \
|
|
${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token | jq -e -r '.access_token')
|
|
|
|
set +e
|
|
|
|
curl --fail-with-body -H "Authorization: bearer ${KEYCLOAK_TOKEN}" "${KEYCLOAK_URL}/admin/realms/cnoe" &> /dev/null
|
|
if [ $? -eq 0 ]; then
|
|
exit 0
|
|
fi
|
|
set -e
|
|
|
|
curl -sS -LO "https://dl.k8s.io/release/v1.28.3//bin/linux/amd64/kubectl"
|
|
chmod +x kubectl
|
|
|
|
echo "creating cnoe realm and groups"
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/realm-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/client-scope-groups-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/group-admin-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/groups
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/group-base-user-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/groups
|
|
|
|
# Create scope mapper
|
|
echo 'adding group claim to tokens'
|
|
CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r '.[] | select(.name == "groups") | .id')
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/group-mapper-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes/${CLIENT_SCOPE_GROUPS_ID}/protocol-mappers/models
|
|
|
|
echo "creating test users"
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/user-user1.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/users
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/user-user2.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/users
|
|
|
|
USER1ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" "${KEYCLOAK_URL}/admin/realms/cnoe/users?lastName=one" | jq -r '.[0].id')
|
|
USER2ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" "${KEYCLOAK_URL}/admin/realms/cnoe/users?lastName=two" | jq -r '.[0].id')
|
|
|
|
echo "setting user passwords"
|
|
jq -r --arg pass ${USER1_PASSWORD} '.value = $pass' /var/config/user-password.json > /tmp/user-password-to-be-applied.json
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X PUT --data @/tmp/user-password-to-be-applied.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/users/${USER1ID}/reset-password
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X PUT --data @/tmp/user-password-to-be-applied.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/users/${USER2ID}/reset-password
|
|
|
|
echo "creating Argo Workflows client"
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/argo-client-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/clients
|
|
|
|
CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r '.[] | select(.clientId == "argo-workflows") | .id')
|
|
CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r '.[] | select(.name == "groups") | .id')
|
|
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X PUT ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
|
|
|
|
ARGO_WORKFLOWS_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')
|
|
|
|
|
|
|
|
|
|
echo "creating Grafana client"
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/grafana-client-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/clients
|
|
|
|
CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r '.[] | select(.clientId == "grafana") | .id')
|
|
|
|
CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r '.[] | select(.name == "groups") | .id')
|
|
curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X PUT ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
|
|
|
|
GRAFANA_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')
|
|
|
|
|
|
|
|
|
|
echo "creating Backstage client"
|
|
curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X POST --data @/var/config/backstage-client-payload.json \
|
|
${KEYCLOAK_URL}/admin/realms/cnoe/clients
|
|
|
|
CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r '.[] | select(.clientId == "backstage") | .id')
|
|
|
|
CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r '.[] | select(.name == "groups") | .id')
|
|
curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X PUT ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
|
|
|
|
BACKSTAGE_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
|
|
-H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
|
|
-X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')
|
|
|
|
ARGOCD_PASSWORD=$(./kubectl -n argocd get secret argocd-initial-admin-secret -o go-template='{{.data.password | base64decode }}')
|
|
|
|
ARGOCD_SESSION_TOKEN=$(curl -k -sS http://argocd-server.argocd.svc.cluster.local:443/api/v1/session -H 'Content-Type: application/json' -d "{\"username\":\"admin\",\"password\":\"${ARGOCD_PASSWORD}\"}" | jq -r .token)
|
|
|
|
echo \
|
|
"apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: keycloak-clients
|
|
namespace: keycloak
|
|
type: Opaque
|
|
stringData:
|
|
ARGO_WORKFLOWS_CLIENT_SECRET: ${ARGO_WORKFLOWS_CLIENT_SECRET}
|
|
ARGO_WORKFLOWS_CLIENT_ID: argo-workflows
|
|
ARGOCD_SESSION_TOKEN: ${ARGOCD_SESSION_TOKEN}
|
|
BACKSTAGE_CLIENT_SECRET: ${BACKSTAGE_CLIENT_SECRET}
|
|
BACKSTAGE_CLIENT_ID: backstage
|
|
GRAFANA_CLIENT_SECRET: ${GRAFANA_CLIENT_SECRET}
|
|
GRAFANA_CLIENT_ID: grafana
|
|
" > /tmp/secret.yaml
|
|
|
|
./kubectl apply -f /tmp/secret.yaml
|
|
|