From 49c4c75c22b20773d4a51356dce892bcc580d085 Mon Sep 17 00:00:00 2001 From: xmlking Date: Wed, 6 Mar 2019 14:44:35 -0800 Subject: [PATCH] feat(deployment): adding k8s deployment docs for postgres and keycloak --- .deploy/keycloak/01-keycloak-configmap.yaml | 18 + .deploy/keycloak/02-keycloak-secrets-tls.yml | 10 + .deploy/keycloak/02-keycloak-secrets.yaml | 13 + .deploy/keycloak/03-keycloak-storage.yaml | 15 + .deploy/keycloak/04-keycloak-deployment.yaml | 82 + .../05-keycloak-service-nodeport.yaml | 16 + .deploy/keycloak/05-keycloak-service.yaml | 15 + .../keycloak/06-keycloak-network-policy.yaml | 16 + .deploy/keycloak/07-keycloak-ingress.yaml | 24 + .deploy/keycloak/keycloak-openshift.yaml | 143 ++ .../keycloak/realm-import/realm-export.json | 1819 +++++++++++++++++ .deploy/kubernetes/crb-admins.yaml | 12 + .deploy/kubernetes/crb-users.yaml | 12 + .deploy/postgres/01-postgres-configmap.yaml | 38 + .deploy/postgres/02-postgres-secrets.yaml | 16 + .deploy/postgres/03-postgres-storage.yaml | 19 + .deploy/postgres/04-postgres-deployment.yaml | 60 + .../05-postgres-service-nodeport.yaml | 16 + .deploy/postgres/05-postgres-service.yaml | 15 + .deploy/postgres/README.md | 88 + .deploy/postgres/debug-busybox.yaml | 23 + .deploy/postgres/deploy.ts | 17 + .deploy/postgres/postgres-openshift.yaml | 98 + 23 files changed, 2585 insertions(+) create mode 100644 .deploy/keycloak/01-keycloak-configmap.yaml create mode 100644 .deploy/keycloak/02-keycloak-secrets-tls.yml create mode 100644 .deploy/keycloak/02-keycloak-secrets.yaml create mode 100644 .deploy/keycloak/03-keycloak-storage.yaml create mode 100644 .deploy/keycloak/04-keycloak-deployment.yaml create mode 100644 .deploy/keycloak/05-keycloak-service-nodeport.yaml create mode 100644 .deploy/keycloak/05-keycloak-service.yaml create mode 100644 .deploy/keycloak/06-keycloak-network-policy.yaml create mode 100644 .deploy/keycloak/07-keycloak-ingress.yaml create mode 100644 .deploy/keycloak/keycloak-openshift.yaml create mode 100644 .deploy/keycloak/realm-import/realm-export.json create mode 100644 .deploy/kubernetes/crb-admins.yaml create mode 100644 .deploy/kubernetes/crb-users.yaml create mode 100644 .deploy/postgres/01-postgres-configmap.yaml create mode 100644 .deploy/postgres/02-postgres-secrets.yaml create mode 100644 .deploy/postgres/03-postgres-storage.yaml create mode 100644 .deploy/postgres/04-postgres-deployment.yaml create mode 100644 .deploy/postgres/05-postgres-service-nodeport.yaml create mode 100644 .deploy/postgres/05-postgres-service.yaml create mode 100644 .deploy/postgres/README.md create mode 100644 .deploy/postgres/debug-busybox.yaml create mode 100644 .deploy/postgres/deploy.ts create mode 100644 .deploy/postgres/postgres-openshift.yaml diff --git a/.deploy/keycloak/01-keycloak-configmap.yaml b/.deploy/keycloak/01-keycloak-configmap.yaml new file mode 100644 index 000000000..857738e84 --- /dev/null +++ b/.deploy/keycloak/01-keycloak-configmap.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: keycloak + labels: + app: keycloak +data: + DB_VENDOR: POSTGRES + DB_ADDR: postgres + DB_PORT: '5432' +# https://issues.jboss.org/browse/KEYCLOAK-7739 + POSTGRES_PORT: '5432' + DB_DATABASE: keycloak + DB_USER: keycloak + KEYCLOAK_USER: admin + ROOT_LOGLEVEL: WARN + KEYCLOAK_LOGLEVEL: INFO + PROXY_ADDRESS_FORWARDING: 'true' diff --git a/.deploy/keycloak/02-keycloak-secrets-tls.yml b/.deploy/keycloak/02-keycloak-secrets-tls.yml new file mode 100644 index 000000000..8d736c7c9 --- /dev/null +++ b/.deploy/keycloak/02-keycloak-secrets-tls.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: keycloak-secrets-tls + labels: + app: keycloak +type: kubernetes.io/tls +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDRENDQWZDZ0F3SUJBZ0lKQUpxYUFOcTRmTGpqTUEwR0NTcUdTSWIzRFFFQkN3VUFNQmt4RnpBVkJnTlYKQkFNTURqRTVNaTR4TmpndU9Ua3VNVEF3TUI0WERURTVNREV6TURJeU5UZ3dOVm9YRFRJd01ERXpNREl5TlRndwpOVm93R1RFWE1CVUdBMVVFQXd3T01Ua3lMakUyT0M0NU9TNHhNREF3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBCkE0SUJEd0F3Z2dFS0FvSUJBUUM2eXc0S0IzR0FFTU85aS9IZFdjUjBpSUlQOE80eUw4eDk3dlBnTUY4emdyTkgKU2Q3eUUvUUo4b1VHUFV4UGR2SllJY0t5bGp0azJuMjZEWDBYTmJzdkRoL2xBbDZBaEwzQ0tYNVowcmFoczhrOQpvc1Z4dGZPOHRBcE0xR3hFNm4wRWk2eU8zRFdrMEhNSW10ZUNranpLWHIwaHhxeUtXckRkSW1MdUIyVEszdmhhClNVVmRhM0t5bFp1ZGZCb1pFalRJdGxORE4zcFdkRjFpZEgxWXhpYmlFMytoU2U3YlNJRi8ydTEySEQ0TjFvUlAKbDdxTmNRUGw4ODN2aFpPRW0wL3g3cklkdTJRMFd2V0FtODZaQVgzMndTcDBTdHRsdjBRT0FSVTJRb2pTMVBLcgpFZlZsWjY4cFR2S1JJc0JDRTViOUdySkR6YnhDa2dqS1pjSFUvRmZaQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXCkJCU3N3ZDMzMzN4d3djME9xdkFOUmt4RG9jY1VNakFmQmdOVkhTTUVHREFXZ0JTc3dkMzMzM3h3d2MwT3F2QU4KUmt4RG9jY1VNakFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUFqU2ZCUgppaGJaM05nTzY5ZDd1b29sSTgrYWo0K29IaldkRjFVRktMNWFaVDRzN2tUVDM3T1p0LzY2YXdwQ2JQOXdwbjdMCjEwMDVZdFRlOU1DclVoVWU4K1luUmplbjAyUit0TjA1SXp5VGZRQWlUa1g0OCs3WVVySGtxZGppN3ZVN21yVXUKNzlxdXNqYWJNQWhDL0RtcnNWWk1wZmxJWm42M2NpZi9KelFlaWpyc2x4dHNPdjRNZDF3RVBHR2lrMjRyY3czagpYSy9YWmQvSGpUT1QvRkwyWkQ2SDAwcHNtb2tnMXp1d3QwS0dweDhrMXFKR04wbzYxdjM5QTVaU2tJcTVSWDlMCk1Ma2NCQjlwRWw3MC83cmZNaFNST3FvZWw4ZVloZW1jTGpYTnhEb3ZhM20yeUFrNVY2eU0ybGMyeExHMlBKdk4KZkJqemsvNDdXdmFraVdZWQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQzZ5dzRLQjNHQUVNTzkKaS9IZFdjUjBpSUlQOE80eUw4eDk3dlBnTUY4emdyTkhTZDd5RS9RSjhvVUdQVXhQZHZKWUljS3lsanRrMm4yNgpEWDBYTmJzdkRoL2xBbDZBaEwzQ0tYNVowcmFoczhrOW9zVnh0Zk84dEFwTTFHeEU2bjBFaTZ5TzNEV2swSE1JCm10ZUNranpLWHIwaHhxeUtXckRkSW1MdUIyVEszdmhhU1VWZGEzS3lsWnVkZkJvWkVqVEl0bE5ETjNwV2RGMWkKZEgxWXhpYmlFMytoU2U3YlNJRi8ydTEySEQ0TjFvUlBsN3FOY1FQbDg4M3ZoWk9FbTAveDdySWR1MlEwV3ZXQQptODZaQVgzMndTcDBTdHRsdjBRT0FSVTJRb2pTMVBLckVmVmxaNjhwVHZLUklzQkNFNWI5R3JKRHpieENrZ2pLClpjSFUvRmZaQWdNQkFBRUNnZ0VBUHAvZE1SZVZ3ZTNjOGl0OUIwMFZSRXlQN25CRUs2ZEMwK1hNOVpqQ0VpNzcKbENiOERBVU1pRms2cUFEZEdMYzBRTC93MnBicmlzQ0Y3bDZmVmF5NStPaXZZTVFPa3QvdURxNkNvZkszTURkcQpmTE9acHFudDhaSWE4ZTgxN1ZVQVhRbkc3STUvbmFZVkt3bGV2M1B2MVprMXA2RklMNHFBU1dOQjc3T2k0ekNTCjBybm82bWJqWlZoLzBDcXdvVE5sMmZSSkJHRCswMjAwWDNYc1N5UlB0WXl1dy94bmhBcWovNTh0V2FsRzVCZ3AKZURDMzdma3RORFF6QnhxYmtOa3NJRm5Mc2lhY3FzaVFveXM5dEh5Z2RZazNBbm1hV0NKMHV0MlpWK1FSZzBITQpYeE44eHRYL1BLSGpsMmoyanA4ZGd5VzQ3S2hTTUdVSnpSSkJUSU4xRVFLQmdRRHMwWk5Qbzd6cUFXYkRCdFhXCkxlS1EyZUIzLzhqQk5VNGloU1R3Q01TdHpjdnVSdzFJRHBUTlQrNWdmN3dhdEo4clVYYks5U0FmZHBBcHNNN0YKK0I1QkU2eTBtZXZPT1pPVUJLalpkVlhDRENiZENJL2FQdmNhTE1CVFFYR2xPWGt3YXg3RHZPaU1iWUJqZStwbgpwU253d09uUmw5VG95WjZ3WEQvaUhHNjVwd0tCZ1FESjdEUVRtR2lEUEpRK21UcnMzTDBBOExpbHRQQXpVb0RnCjZOZU1MbjFvanpmbnFCK0hoY3ArUFRkTEdaL3UvdEJoSlBpMjhGYnA5d29UZjZsOVRUeTdiWXVJTmhnR0FFOTUKTllmWmdZRnoxeG81NWFRMGlVdUlTU2VLd2tlckUwalhjZDJHUS9BVTNJS2pHNWsyN0NyODJOYW1ZMWprVmNYaApWSnVkMWd1U2Z3S0JnUURmc3EvbUlKTUlzRDR6cHpkQzA2RWFsdFIwUmNlS3lpRitkaXFJR3FBK1ZSRjNpT0pBCjJKVXl3M2cyNnpIeVAvL0NXWGRQQy9GR0J5YnlXck5YU2FLNmNpMUhnOFc4emFKYnhYTUJ4dFkxTUdsZGpRTGwKYzJqUzdzYlZYaEpOc2xhSnFBWXRlenBudTkvMUdVNDRqVWxxdzYrYWJrT05pVFkrL1VUUy8yeVFMUUtCZ1FDUwptODJ6N0pUMXRLWnVUUk5icDJvVHBMUnNuRk5LOER4akVLeHdPOUVTZWVvOWFQUjdrY2pVbjkzbHlWVTJVZzA2CmUzMmFWaWJFK1hsM2J0bkJBTE4zNHJITTFIbmM0WnpTaE90K0lXbThraEdZdUFQaG9VNFdSZ2RQMkgvdzFxK3MKdmgvNXZKbzBpMmJPeHRzWDVhSFF0Q0FsN3p3N2pZdC9iODhOYXJ3OFFRS0JnRnprS2hUT3FETlVCL1ErSWJqMwpoeDNEbVA3bzNxNURkWWlITTU5bTVsN0NNK3dmWTF6Wmtlb3RQZGpsZEZZbDdEdWx3TjFjUlRSbnk2YkxrcXl3CmlwYlNHQ3ZTQ0NTUmFxeU9WckR6UENFUkdvbXVqNjhlcXl0a2Eyck9lWnB5SStFUWtTZnNlMVpmUXRjTjA4Q0IKZy9VMGZpNm1pdzBvQmVCZ3FLMElwS3V0Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K diff --git a/.deploy/keycloak/02-keycloak-secrets.yaml b/.deploy/keycloak/02-keycloak-secrets.yaml new file mode 100644 index 000000000..ae8228a38 --- /dev/null +++ b/.deploy/keycloak/02-keycloak-secrets.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: keycloak + labels: + app: keycloak +type: Opaque +data: + KEYCLOAK_PASSWORD: YWRtaW4xMjM= + DB_PASSWORD: a2V5Y2xvYWsxMjM= +stringData: + KEYCLOAK_PASSWORD: admin123 + DB_PASSWORD: keycloak123 diff --git a/.deploy/keycloak/03-keycloak-storage.yaml b/.deploy/keycloak/03-keycloak-storage.yaml new file mode 100644 index 000000000..f8ceae1ae --- /dev/null +++ b/.deploy/keycloak/03-keycloak-storage.yaml @@ -0,0 +1,15 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: keycloak + labels: + app: keycloak +spec: +# use hostpath when deploying local k8s and use nas-thin for k8s cluster + storageClassName: hostpath + # storageClassName: nas-thin + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi diff --git a/.deploy/keycloak/04-keycloak-deployment.yaml b/.deploy/keycloak/04-keycloak-deployment.yaml new file mode 100644 index 000000000..9b023b6d8 --- /dev/null +++ b/.deploy/keycloak/04-keycloak-deployment.yaml @@ -0,0 +1,82 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: keycloak + labels: + app: keycloak +spec: + replicas: 1 + template: + metadata: + labels: + app: keycloak + spec: + securityContext: + runAsUser: 1000 + fsGroup: 1000 + runAsNonRoot: true + terminationGracePeriodSeconds: 60 + initContainers: + - name: wait-for-postgresql + image: busybox + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + until printf "." && nc -z -w 2 postgres 5432; do + sleep 2; + done; + + echo 'PostgreSQL OK ✓' + containers: + - name: keycloak + image: jboss/keycloak:4.8.3.Final + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8080 + protocol: TCP + envFrom: + - secretRef: + name: keycloak + - configMapRef: + name: keycloak + livenessProbe: + httpGet: + path: /auth + port: http + initialDelaySeconds: 120 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: auth/realms/master + port: http + initialDelaySeconds: 30 + timeoutSeconds: 1 +# volumeMounts: +# - mountPath: /opt/jboss/keycloak/standalone/data +# name: keycloak-data-volume +# readOnly: false +# - mountPath: "/etc/x509/https" +# name: tls-config-volume +# readOnly: true +# - mountPath: /opt/jboss/keycloak/standalone/configuration/import +# name: keycloak-import-volume +# readOnly: true +# volumes: +# - name: keycloak-data-volume +# persistentVolumeClaim: +# claimName: keycloak +# - name: tls-config-volume +# secret: +# secretName: keycloak-secrets-tls +# - name: keycloak-import-volume +# configMap: +# defaultMode: 0420 +# name: keycloak-config-imports +# items: +# - key: realm-export.json +# path: realm-export.json +# - key: standalone.xml +# path: standalone.xml diff --git a/.deploy/keycloak/05-keycloak-service-nodeport.yaml b/.deploy/keycloak/05-keycloak-service-nodeport.yaml new file mode 100644 index 000000000..e1bef3119 --- /dev/null +++ b/.deploy/keycloak/05-keycloak-service-nodeport.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + type: NodePort + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: http + nodePort: 31080 + selector: + app: keycloak diff --git a/.deploy/keycloak/05-keycloak-service.yaml b/.deploy/keycloak/05-keycloak-service.yaml new file mode 100644 index 000000000..38bfea4b8 --- /dev/null +++ b/.deploy/keycloak/05-keycloak-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: keycloak + labels: + app: keycloak +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: keycloak diff --git a/.deploy/keycloak/06-keycloak-network-policy.yaml b/.deploy/keycloak/06-keycloak-network-policy.yaml new file mode 100644 index 000000000..5fb4a9030 --- /dev/null +++ b/.deploy/keycloak/06-keycloak-network-policy.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: keycloak + labels: + app: keycloak +spec: + podSelector: + matchLabels: + app: keycloak + policyTypes: + - Ingress + ingress: + - ports: + - protocol: TCP + port: 8080 diff --git a/.deploy/keycloak/07-keycloak-ingress.yaml b/.deploy/keycloak/07-keycloak-ingress.yaml new file mode 100644 index 000000000..68a667d31 --- /dev/null +++ b/.deploy/keycloak/07-keycloak-ingress.yaml @@ -0,0 +1,24 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: keycloak-ingress + labels: + app: keycloak +spec: + to: + kind: Service + name: keycloak + port: + targetPort: http + tls: + - hosts: + - keycloak.example.com + secretName: keycloak-secrets-tls + rules: + - host: keycloak.example.com + http: + paths: + - path: / + backend: + serviceName: keycloak + servicePort: http diff --git a/.deploy/keycloak/keycloak-openshift.yaml b/.deploy/keycloak/keycloak-openshift.yaml new file mode 100644 index 000000000..13be8eed8 --- /dev/null +++ b/.deploy/keycloak/keycloak-openshift.yaml @@ -0,0 +1,143 @@ +apiVersion: v1 +kind: List +items: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + annotations: {} + name: keycloak-data + labels: + app: keycloak + spec: +# storageClassName: nas-thin + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi + + - apiVersion: v1 + kind: ConfigMap + metadata: + labels: + app: keycloak + name: keycloak-config + data: + keycloak_user: admin + db_vendor: H2 + + - apiVersion: v1 + kind: Secret + metadata: + name: keycloak-secrets + labels: + app: keycloak + stringData: + keycloak_password: admin123 + + - apiVersion: v1 + kind: Service + metadata: + name: keycloak + labels: + app: keycloak + spec: + ports: + - name: keycloak-8080-http + port: 8080 + protocol: TCP + targetPort: 8080 + selector: + app: keycloak + + - apiVersion: route.openshift.io/v1 + kind: Route + metadata: + name: keycloak + labels: + expose: "true" + app: keycloak + spec: + to: + kind: Service + name: keycloak + port: + targetPort: keycloak-8080-http + tls: + termination: edge + + - apiVersion: apps.openshift.io/v1 + kind: DeploymentConfig + metadata: + name: keycloak + labels: + app: keycloak + spec: + replicas: 1 + strategy: + type: Rolling + triggers: + - type: ConfigChange + template: + metadata: + name: keycloak + labels: + app: keycloak + spec: + containers: + - name: keycloak + image: docker.io/jboss/keycloak:4.8.3.Final + imagePullPolicy: IfNotPresent + resources: + limits: + memory: 4G + requests: + memory: 2G + ports: + - containerPort: 8080 + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: KEYCLOAK_USER + valueFrom: + configMapKeyRef: + name: keycloak-config + key: keycloak_user + - name: KEYCLOAK_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-secrets + key: keycloak_password + - name: PROXY_ADDRESS_FORWARDING + value: "true" + livenessProbe: + httpGet: + path: /auth/realms/master + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /auth/realms/master + port: 8080 + scheme: HTTP + initialDelaySeconds: 10 + timeoutSeconds: 10 + failureThreshold: 3 + volumeMounts: + - mountPath: /opt/jboss/keycloak/standalone/data + name: keycloak-data + readOnly: false + securityContext: + privileged: false + volumes: + - name: keycloak-data + persistentVolumeClaim: + claimName: keycloak-data + + diff --git a/.deploy/keycloak/realm-import/realm-export.json b/.deploy/keycloak/realm-import/realm-export.json new file mode 100644 index 000000000..b3ec389e8 --- /dev/null +++ b/.deploy/keycloak/realm-import/realm-export.json @@ -0,0 +1,1819 @@ +{ + "id": "ngx", + "realm": "ngx", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "93a1f354-6f49-47be-9caf-c4fd07d0da9b", + "name": "ROLE_ADMIN", + "composite": false, + "clientRole": false, + "containerId": "ngx", + "attributes": {} + }, + { + "id": "ccac0537-5967-4c04-a93e-404af3358e7e", + "name": "ROLE_USER", + "composite": false, + "clientRole": false, + "containerId": "ngx", + "attributes": {} + }, + { + "id": "728cb2d9-b945-4845-8034-2c8399c78114", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "ngx", + "attributes": {} + }, + { + "id": "a189e1bb-7637-4793-81b5-92bc656e5937", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "ngx", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "238be9b8-bc50-4b70-bc4d-29c523c42b86", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "62624bef-66df-4fda-934b-fd56514930a5", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "f08bbd27-5dc0-4bca-b2f3-b1416426a3c4", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "596b8727-162f-49d7-a512-b13138eceb56", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "8e1691a8-f362-4c0b-b049-3170b7d7f6ac", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "457cd9ce-b694-4f57-a6b3-849dd90b66d0", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-groups", + "query-users" + ] + } + }, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "c7ca3379-04b0-4b46-9a49-b7979b379f41", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "beb62116-850a-4850-9c5b-6221450ce12a", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "543adbd0-a678-4be3-b975-52953f6ab429", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "33c1fad4-5bbe-4d03-ae71-ed5c6ca41384", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "826f89c0-252c-4228-931e-3605b6e09ec9", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "4895a697-98c3-4398-9caf-3ae782c73d58", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "4cc7fb8e-5c5a-4ded-8c2d-518799092328", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "cae0b1bd-93bb-43d6-983e-91ba289c5a4a", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "5becd7e4-0863-4d63-bdbe-cafeb16807d3", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "1b033264-ce35-42d1-80c5-73d232a5c0fb", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "ce3fb35e-6480-4499-bed6-e4a500aa2819", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "ff50cc26-d43d-40dd-a720-a9dab868d996", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "create-client", + "manage-events", + "view-identity-providers", + "manage-identity-providers", + "view-authorization", + "view-users", + "impersonation", + "query-realms", + "manage-users", + "manage-realm", + "manage-authorization", + "query-groups", + "query-clients", + "query-users", + "view-realm", + "view-clients", + "manage-clients", + "view-events" + ] + } + }, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + }, + { + "id": "41ed6567-afa5-4c5b-b65e-fcd88dd1713a", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "b33b6774-094b-42c5-b556-7c2f268fab19", + "attributes": {} + } + ], + "ngxapi": [], + "security-admin-console": [], + "admin-cli": [], + "ngxapp": [], + "broker": [ + { + "id": "45bf247f-b7ea-4a17-b196-2f90ac8eccb6", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "d18d6aab-febe-4099-a483-4ea712eb747c", + "attributes": {} + } + ], + "account": [ + { + "id": "e431a18d-18f0-472f-a37c-57ea7f24d3d0", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "774d9e19-8bef-48ec-a5af-c80dd35f92e8", + "attributes": {} + }, + { + "id": "d4a04b74-2620-4616-ae67-be31f807c5b9", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "774d9e19-8bef-48ec-a5af-c80dd35f92e8", + "attributes": {} + }, + { + "id": "48aaa49c-6bc0-4854-81e2-1ed2d4e301f7", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "774d9e19-8bef-48ec-a5af-c80dd35f92e8", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": [ + "offline_access", + "uma_authorization" + ], + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clients": [ + { + "id": "8cbc243e-bb98-40c1-b4f1-faecaa42250b", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "54d724fb-b1bf-491d-a92d-d0b1d5c61190", + "clientId": "ngxapi", + "rootUrl": "http://localhost:3000", + "adminUrl": "http://localhost:3000", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "ngxapi_audience", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "774d9e19-8bef-48ec-a5af-c80dd35f92e8", + "clientId": "account", + "name": "${client_account}", + "baseUrl": "/auth/realms/ngx/account", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "defaultRoles": [ + "view-profile", + "manage-account" + ], + "redirectUris": [ + "/auth/realms/ngx/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "b33b6774-094b-42c5-b556-7c2f268fab19", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "202f8bde-2673-41a7-bede-f8ac28a6dce8", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "baseUrl": "/auth/admin/ngx/console/index.html", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "/auth/admin/ngx/console/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "6640ff35-a5fa-4005-afac-de3ad9248cf3", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "d18d6aab-febe-4099-a483-4ea712eb747c", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + }, + { + "id": "805899f7-f443-45c8-b404-beb0f50a890a", + "clientId": "ngxapp", + "rootUrl": "http://localhost:4200", + "adminUrl": "http://localhost:4200", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "ngxapi_audience", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access" + ] + } + ], + "clientScopes": [ + { + "id": "40e7813e-6e9c-43f4-bd46-e41cbabd977e", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "725e94f9-dca3-48d7-a451-20a24245a2bc", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "7b0b974a-fb17-419e-bf9e-5059e7d821ad", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "4f7f5a30-05a9-4a85-bce9-b1547b2b5d76", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "ecb65ba1-ca65-442d-8038-f389f7ad0383", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "330df02a-967c-4dfa-8d1a-e8c7ef7cc8ce", + "name": "ngxapi_audience", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "ffbaf1b6-454d-4f44-a465-8f64a675adfe", + "name": "ngxapi_audience_mapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "ngxapi", + "id.token.claim": "false", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "faae796f-dae5-4b60-816e-7abd9619e9c4", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "9c45732e-5a92-4cc0-8d30-d33883535693", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "381a4e21-b995-498f-bf32-820963723baa", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "0458f778-2e33-4c3c-b0b3-ebd3802dd081", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "3f434c8a-ed3f-48c0-8098-f52ac7a2fee2", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "92aa27b7-7922-469c-beda-928129bf1dcb", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "19f89572-76d7-44a8-b592-d5a2053137b9", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "e6447075-bf4b-4c40-ad6d-259e52c456d8", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "744d4e15-29c0-46d8-bcda-4195b6507a95", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "9fafb3f3-9eb5-4aa5-8007-787d8e8f5187", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "4c3ab919-0569-4939-87d3-5369df7f52d5", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "55122475-ef7d-4f15-a7d5-628eccc8292e", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "61102fe0-b3b3-40c0-9211-01a4f3eb5319", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "082e0063-33f9-4b39-bdb2-884190cd3d67", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "a9ad59c4-2dda-4f5b-8084-1afa7cfa8935", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "64951fcf-ded4-4288-a5aa-f1d8d52ac1be", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "e60598cd-58d3-4f55-b104-9f9767945a34", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "81fb3c26-deab-44a1-bfd7-779b9474bd95", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "0e2501f1-6eeb-4f59-8139-af16cb4d970c", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "a78d149b-c12f-4864-881e-0a8d28e174fc", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "e6357190-19b3-407f-8e6f-48cf55b02988", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "72151e68-4963-4967-a48e-268afd5b933e", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "e7547eaa-19ae-4b30-a2cc-a412532d06c4", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + }, + { + "id": "a30a29fa-15b7-491d-87da-c1dc4db3144b", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "86d26b72-9bf4-41a9-b05a-b06cb1301c6c", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "10bcbe45-c51b-4dbc-8e04-2358350200b8", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "faebebd5-e4cf-4bb0-9075-2982845d8daa", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "web-origins", + "profile", + "roles", + "email", + "role_list" + ], + "defaultOptionalClientScopes": [ + "address", + "phone", + "offline_access" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "faa76ce3-08e4-4378-ba94-35cde59da8a1", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper" + ] + } + }, + { + "id": "b3871ad6-6a6d-4c23-989d-a07171146b8e", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "cb271f85-f104-43c7-88c8-0a1141a8e6c7", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "74a75201-3559-4422-93e0-9422b96f3334", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "cee01fb8-8024-4b59-9cc6-762cb7850837", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "4a10690e-122c-401a-906a-6355148ed310", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "e2f6748f-6940-49e8-9790-b1d1939a4dc8", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "e78d90f2-6e83-4aa2-b715-a905294d2dc2", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "57bcf63c-fa01-49b8-86f0-c994040d5060", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "a7726385-db98-4832-bcd9-a710ce03c716", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "efd54847-2cb8-47b8-8d24-276b8532cc49", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "cdbac843-fa59-44b8-ae93-73f2e911b408", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d5c386bc-5963-4aac-aee2-1676fc89de98", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "e16e7d7c-b0bd-4030-9b2b-9c67d4dfe711", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "4a5c66f2-df4e-4405-856c-8a0bd27d08f3", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "600cd15f-f5a5-4fbc-96b2-3878d3031b0c", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "OPTIONAL", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "a20b4bf9-11d8-49cd-abf2-6c457442e652", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "b7d273ed-4507-4327-8b53-f0c0b3f4889b", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "c857456e-8e56-4189-95bc-69203fa7892c", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "10fda3c3-0d56-4526-a074-dad2bb0c5826", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "65682d20-3a02-40d0-af6f-019059597aef", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "aaca4479-e8a9-4510-9a63-f9a2d30dcdb8", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "409fefae-78dc-4f24-94db-5444a44025da", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "OPTIONAL", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "0c3bb9de-d9f3-4ba7-8b7c-dc4181108b79", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "980fe19e-3c81-4e6c-a55f-27dc28580d08", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "09c643f1-07e8-4da4-ae77-8f234bd926e0", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "_browser_header.xXSSProtection": "1; mode=block", + "_browser_header.xFrameOptions": "SAMEORIGIN", + "_browser_header.strictTransportSecurity": "max-age=31536000; includeSubDomains", + "permanentLockout": "false", + "quickLoginCheckMilliSeconds": "1000", + "_browser_header.xRobotsTag": "none", + "maxFailureWaitSeconds": "900", + "minimumQuickLoginWaitSeconds": "60", + "failureFactor": "30", + "actionTokenGeneratedByUserLifespan": "300", + "maxDeltaTimeSeconds": "43200", + "_browser_header.xContentTypeOptions": "nosniff", + "offlineSessionMaxLifespan": "5184000", + "actionTokenGeneratedByAdminLifespan": "43200", + "_browser_header.contentSecurityPolicyReportOnly": "", + "bruteForceProtected": "false", + "_browser_header.contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "waitIncrementSeconds": "60", + "offlineSessionMaxLifespanEnabled": "false" + }, + "keycloakVersion": "4.8.3.Final", + "userManagedAccessAllowed": false +} \ No newline at end of file diff --git a/.deploy/kubernetes/crb-admins.yaml b/.deploy/kubernetes/crb-admins.yaml new file mode 100644 index 000000000..f03b93d27 --- /dev/null +++ b/.deploy/kubernetes/crb-admins.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: keycloak-cluster-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: oidc:/cluster-admins diff --git a/.deploy/kubernetes/crb-users.yaml b/.deploy/kubernetes/crb-users.yaml new file mode 100644 index 000000000..8ed7bbfba --- /dev/null +++ b/.deploy/kubernetes/crb-users.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: keycloak-cluster-users +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: view +subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: oidc:/cluster-users diff --git a/.deploy/postgres/01-postgres-configmap.yaml b/.deploy/postgres/01-postgres-configmap.yaml new file mode 100644 index 000000000..336cac5a6 --- /dev/null +++ b/.deploy/postgres/01-postgres-configmap.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres + labels: + app: postgres +data: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + COCKPIT_POSTGRES_DB: cockpit + COCKPIT_POSTGRES_USER: cockpit + KEYCLOAK_POSTGRES_DB: keycloak + KEYCLOAK_POSTGRES_USER: keycloak + db-setup-scripts.sh: |- + #!/bin/bash + + set -e + set -u + + function create_user_and_database() { + local database=$1 + local username=$2 + local password=$3 + echo " Creating user '$username' and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE ROLE $username WITH PASSWORD '$password' NOSUPERUSER LOGIN; + CREATE DATABASE $database; + GRANT ALL PRIVILEGES ON DATABASE $database TO $username; + EOSQL + } + + if [ -n "$COCKPIT_POSTGRES_DB" ]; then + create_user_and_database $COCKPIT_POSTGRES_DB $COCKPIT_POSTGRES_USER $COCKPIT_POSTGRES_PASSWORD + fi + if [ -n "$KEYCLOAK_POSTGRES_DB" ]; then + create_user_and_database $KEYCLOAK_POSTGRES_DB $KEYCLOAK_POSTGRES_USER $KEYCLOAK_POSTGRES_PASSWORD + fi + diff --git a/.deploy/postgres/02-postgres-secrets.yaml b/.deploy/postgres/02-postgres-secrets.yaml new file mode 100644 index 000000000..2792b8a15 --- /dev/null +++ b/.deploy/postgres/02-postgres-secrets.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Secret +metadata: + name: postgres + labels: + app: postgres +type: Opaque +data: + POSTGRES_PASSWORD: cG9zdGdyZXMzMjE= + KEYCLOAK_POSTGRES_PASSWORD: a2V5Y2xvYWsxMjM= + COCKPIT_POSTGRES_PASSWORD: Y29ja3BpdDEyMw== + +stringData: + POSTGRES_PASSWORD: postgres321 + KEYCLOAK_POSTGRES_PASSWORD: keycloak123 + COCKPIT_POSTGRES_PASSWORD: cockpit123 diff --git a/.deploy/postgres/03-postgres-storage.yaml b/.deploy/postgres/03-postgres-storage.yaml new file mode 100644 index 000000000..379f6ae10 --- /dev/null +++ b/.deploy/postgres/03-postgres-storage.yaml @@ -0,0 +1,19 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: postgres + labels: + app: postgres + annotations: + trident.netapp.io/reclaimPolicy: "Retain" + trident.netapp.io/snapshotDirectory: "false" + trident.netapp.io/snapshotPolicy: "default" +spec: +# use hostpath when deploying local k8s and use nas-thin for k8s cluster + storageClassName: hostpath +# storageClassName: nas-thin + accessModes: + - ReadWriteMany + resources: + requests: + storage: 10Gi diff --git a/.deploy/postgres/04-postgres-deployment.yaml b/.deploy/postgres/04-postgres-deployment.yaml new file mode 100644 index 000000000..b664874b9 --- /dev/null +++ b/.deploy/postgres/04-postgres-deployment.yaml @@ -0,0 +1,60 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: postgres + labels: + app: postgres +spec: + replicas: 1 + template: + metadata: + labels: + app: postgres + spec: + securityContext: + runAsUser: 999 + fsGroup: 999 + containers: + - name: postgres + image: postgres:11 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5432 + envFrom: + - secretRef: + name: postgres + - configMapRef: + name: postgres + livenessProbe: + exec: + command: ["pg_isready", "-U", "postgres", "-d", "postgres"] + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + readinessProbe: + exec: + command: ["pg_isready", "-U", "postgres", "-d", "postgres"] + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + volumeMounts: + - mountPath: /var/lib/postgresql/ + name: postgres-data-volume + - mountPath: /docker-entrypoint-initdb.d/ + name: db-setup-scripts-volume + readOnly: true + volumes: + - name: postgres-data-volume + persistentVolumeClaim: + claimName: postgres + - name: db-setup-scripts-volume + configMap: + defaultMode: 0750 + name: postgres + items: + - key: db-setup-scripts.sh + path: db-setup-scripts.sh diff --git a/.deploy/postgres/05-postgres-service-nodeport.yaml b/.deploy/postgres/05-postgres-service-nodeport.yaml new file mode 100644 index 000000000..af767a041 --- /dev/null +++ b/.deploy/postgres/05-postgres-service-nodeport.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app: postgres +spec: + type: NodePort + ports: + - name: postgresql + port: 5432 + protocol: TCP + targetPort: postgresql + nodePort: 35432 + selector: + app: postgres diff --git a/.deploy/postgres/05-postgres-service.yaml b/.deploy/postgres/05-postgres-service.yaml new file mode 100644 index 000000000..8c280a3ae --- /dev/null +++ b/.deploy/postgres/05-postgres-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app: postgres +spec: + type: ClusterIP + ports: + - name: postgresql + port: 5432 + protocol: TCP + targetPort: 5432 + selector: + app: postgres diff --git a/.deploy/postgres/README.md b/.deploy/postgres/README.md new file mode 100644 index 000000000..1f9a7aa71 --- /dev/null +++ b/.deploy/postgres/README.md @@ -0,0 +1,88 @@ +# PostgreSQL + +Deploying **Postgre** as datastore for _APIs_ and _KeyCloak_ + +### Prerequisites + +- Working Kubernetes Cluster +- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) or [oc](https://docs.openshift.com/container-platform/3.11/cli_reference/get_started_cli.html) utility installed +- [StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/) defined on your Kubernetes instance +- install postgres cli via `brew install postgresql` + +### Deploy + +#### Deploying to Kubernetes + +> 1. assume you already setup `ngx` kubernetes context +> 2. make sure you current context is using correct `namespace`. i.e., `kubectl config current-context` +> 3. make sure keycloak image `version` is correct in `*-deployment.yaml` file. +> 4. generate base64 passwords for `*-secrets.yaml` with `echo -n 'admin' | base64` + +```bash +cd .deploy/postgres + +# create configmap +kubectl create -f 01-postgres-configmap.yaml +# verify +kubectl get configmap postgres -o yaml + +# create secrets +kubectl create -f 02-postgres-secrets.yaml +# verify secrets +kubectl get secret postgres -o yaml + +# create persistentvolumeclaim +kubectl create -f 03-postgres-storage.yaml +# verify persistentvolumeclaim +kubectl get persistentvolumeclaim --namespace default +kubectl get persistentvolume + +# create deployment +kubectl create -f 04-postgres-deployment.yaml +# verify deployment +kubectl describe pod postgres +kubectl get deployment postgres -o yaml +kubectl get po -o wide --watch + +MY_POD=$(kubectl get pods -lapp=postgres -o jsonpath='{.items[0].metadata.name}') +kubectl log $MY_POD -f +kubectl exec -it $MY_POD -- /bin/bash +# if you have to copy something use `kubectl cp` +kubectl cp /Developer/Work/SPA/ngx-starter-kit/.deploy/postgres/scripts/create_databases.sh $MY_POD:/tmp/test.sh + +# create service (use -service.yaml for prod cluster, -nodeport.yaml for development) +kubectl create -f 05-postgres-service.yaml +# verify service +kubectl get svc postgres +kubectl get ep + +# create network policy(optional - if your cluster is enabled for network-policy) +kubectl create -f 06-keycloak-network-policy.yaml + +kubectl get all,configmap,secret -l app=postgres +``` + +### Connect to PostgreSQL + +```bash +kubectl port-forward $MY_POD 5432:5432 +psql -h localhost -p 5432 -U cockpit --password -d cockpit +``` + +#### Delete PostgreSQL Deployment + +```bash +kubectl delete service postgres +kubectl delete deployment postgres +kubectl delete configmap postgres +kubectl delete secret postgres +kubectl delete persistentvolumeclaim postgres +``` + +#### TODO + +> deploy via typescript + +```bash +ts-node .deploy/postgres/deploy.ts +``` diff --git a/.deploy/postgres/debug-busybox.yaml b/.deploy/postgres/debug-busybox.yaml new file mode 100644 index 000000000..c2b05c0bb --- /dev/null +++ b/.deploy/postgres/debug-busybox.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Pod +metadata: + name: debug-busybox +spec: + securityContext: + runAsUser: 60217 + fsGroup: 53134 + restartPolicy: Never + containers: + - name: debug-busybox + image: busybox + args: + - sleep + - "100000" + volumeMounts: + - mountPath: /data + name: postgres-data-volume + readOnly: false + volumes: + - name: postgres-data-volume + persistentVolumeClaim: + claimName: postgres diff --git a/.deploy/postgres/deploy.ts b/.deploy/postgres/deploy.ts new file mode 100644 index 000000000..fb2da3ea1 --- /dev/null +++ b/.deploy/postgres/deploy.ts @@ -0,0 +1,17 @@ +const Client = require('kubernetes-client').Client; +const config = require('kubernetes-client').config; + +const client = new Client({ config: config.fromKubeconfig(), version: '1.10' }); + +(async () => { + + try { + const namespaces = await client.api.v1.namespaces.get(); + namespaces.body.items.forEach( (namespace) => { + console.log(namespace.metadata.name); + }); + } catch (error) { + console.log(error); + } + +})(); diff --git a/.deploy/postgres/postgres-openshift.yaml b/.deploy/postgres/postgres-openshift.yaml new file mode 100644 index 000000000..50f18b6b9 --- /dev/null +++ b/.deploy/postgres/postgres-openshift.yaml @@ -0,0 +1,98 @@ +apiVersion: "v1" +kind: "PersistentVolumeClaim" +metadata: + name: "postgresql" +spec: + accessModes: + - "ReadWriteMany" + resources: + requests: + storage: "10Gi" + +--- + +apiVersion: v1 +kind: Service +metadata: + name: postgres +spec: + ports: + - name: pgql + port: 5432 + targetPort: 5432 + protocol: TCP + selector: + name: postgresql + +--- + +apiVersion: apps.openshift.io/v1 +kind: DeploymentConfig +metadata: + annotations: + template.alpha.openshift.io/wait-for-ready: 'true' + labels: + app: postgresql-persistent + template: postgresql-persistent-template + name: postgresql +spec: + replicas: 1 + selector: + name: postgresql + strategy: + type: Recreate + template: + metadata: + labels: + name: postgresql + spec: + containers: + - env: + - name: POSTGRESQL_USER + value: kong + - name: POSTGRESQL_PASSWORD + value: kong + - name: POSTGRESQL_DATABASE + value: kong + image: postgres:11 + livenessProbe: + exec: + command: + - /bin/sh + - '-i' + - '-c' + - pg_isready -h 127.0.0.1 -p 5432 + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: postgresql + ports: + - containerPort: 5432 + protocol: TCP + readinessProbe: + exec: + command: + - /bin/sh + - '-i' + - '-c' + - >- + psql -h 127.0.0.1 -U kong -q -d + kong -c 'SELECT 1' + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + volumeMounts: + - mountPath: /var/lib/pgsql/data + name: postgresql-data + volumes: + - name: postgresql-data + persistentVolumeClaim: + claimName: postgresql + test: false + triggers: + - type: ConfigChange +