Automated client registration method 1
IBM Cloud Pak® for Multicloud Management uses OpenID Connect (OIDC) to allow integrated workloads to use integrated authentication in IBM Cloud Pak for Multicloud Management. You can obtain the OAuth secret after you deploy a Helm chart.
You can register with the OIDC and obtain the secret after the Helm chart is deployed. Registration during a deployment is not recommended because it requires the chart installer or the application service account to have cluster administrator authority and requires cross-namespace access.
As part of the Helm chart deployment, a container, which includes the logic that is used to register with OIDC, is deployed.
Following is a sample users-config.yaml
file:
{{/*********************************************************** {COPYRIGHT-TOP} ****
* Licensed Materials - Property of IBM
*
* "Restricted Materials of IBM"
*
* 5737-H89, 5737-H64
*
* © Copyright IBM Corp. 2015, 2018 All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication, or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
********************************************************* {COPYRIGHT-END} ****/}}
{{- $compName := "cem-users" -}}
{{- include "sch.config.init" (list . "cem.sch.chart.config.values") -}}
{{- $configMapName := include "sch.names.fullCompName" (list . $compName) -}}
kind: ConfigMap
metadata:
name: {{ $configMapName }}
namespace: {{ .Release.Namespace }}
labels:
{{ include "sch.metadata.labels.standard" (list .) | indent 4 }}
origin: helm-cem
apiVersion: v1
data:
oidcPayload.json: |-
{
"token_endpoint_auth_method":"client_secret_basic",
"client_id": "${client_id}",
"client_secret": "${client_secret}",
"scope":"openid profile email",
"grant_types":[
"authorization_code",
"client_credentials",
"password",
"implicit",
"refresh_token",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
],
"response_types":[
"code",
"token",
"id_token token"
],
"application_type":"web",
"subject_type":"public",
"post_logout_redirect_uris":[
"https://{{ .Values.global.masterIP }}:{{ .Values.global.masterPort }}/console/logout"
],
"preauthorized_scope":"openid profile email general",
"introspect_tokens":true,
"trusted_uri_prefixes":[
"https://{{ .Values.global.masterIP }}:{{ .Values.global.masterPort }}"
],
"redirect_uris":[
"https://{{ .Values.global.masterIP }}:{{ .Values.global.masterPort }}/auth/liberty/callback",
"https://{{ .Values.global.ingress.domain }}/{{ .Values.global.ingress.prefix }}",
"https://{{ .Values.global.ingress.domain }}/{{ .Values.global.ingress.prefix }}users/api/authprovider/v1/icp/return"
]
}
oidc_reg.sh: |-
#!/bin/bash
#validate no: of args
if [[ "$1" == "?" ]]; then
echo "USAGE: oidc_reg.sh { OIDC_CREDENTIALS }"
echo
echo "Run as cluster admin from your master node to register credentials for Cloud App Management."
echo
exit 1
else
echo "Registering IBM Cloud Event Management identity ..."
fi
OIDC_CREDENTIALS="$1"
OIDC_CREDENTIALS="$(base64 --decode <<< $OIDC_CREDENTIALS)"
if [ -z "$OIDC_CREDENTIALS" ] ; then
echo "✖ Error: OIDC_CREDENTIALS not valid!" >&2;
echo
exit 1
fi
echo
#replace variables in OIDC_PAYLOAD and write to a temp file
sed -e "s/\${client_id}/$AUTH_ICP_CLIENT_ID/" -e "s/\${client_secret}/$AUTH_ICP_CLIENT_SECRET/" /etc/oidc/oidcPayload.json > /tmp/OIDC_PAYLOAD_TEMP
#Check registration
echo "Checking registration..."
REG_CHECK_RESULT="$(curl -k -X GET -u "Authorization: Bearer ${ACCESS_TOKEN}" -H "Content-Type: application/json" https://{{ .Values.global.masterIP }}:8443/idprovider/v1/auth/registration/$AUTH_ICP_CLIENT_ID)"
echo
#echo REG_CHECK_RESULT:
#echo $REG_CHECK_RESULT
#echo
if [[ $REG_CHECK_RESULT = *"access_denied"* ]]; then
echo "✖ Authentication failed, must be a admin"
echo
fi
if [[ $REG_CHECK_RESULT = *"client_id_issued_at"* ]]; then
echo "✔ Client exists..."
#Update registration
echo "Updating registration..."
curl -k -X PUT -u "Authorization: Bearer ${ACCESS_TOKEN}" -H "Content-Type: application/json" --data @/tmp/OIDC_PAYLOAD_TEMP https://{{ .Values.global.masterIP }}:8443/idprovider/v1/auth/registration/$AUTH_ICP_CLIENT_ID >/dev/null
echo
fi
if [[ $REG_CHECK_RESULT = *"invalid_client"* ]]; then
echo "✖ Client does not exist..."
#Create registration
echo "Registering client..."
curl -k -X POST -u "Authorization: Bearer ${ACCESS_TOKEN}" -H "Content-Type: application/json" --data @/tmp/OIDC_PAYLOAD_TEMP https://{{ .Values.global.masterIP }}:8443/idprovider/v1/auth/registration >/dev/null
echo
fi
#Cleanup - delete the temp payload file with variables replaced
trap "{ rm -f /tmp/OIDC_PAYLOAD_TEMP; }" EXIT
echo
echo Done.
Following is a sample users.yaml
file:
{{/*********************************************************** {COPYRIGHT-TOP} ****
* Licensed Materials - Property of IBM
*
* "Restricted Materials of IBM"
*
* 5737-H89, 5737-H64
*
* © Copyright IBM Corp. 2015, 2018 All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication, or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
********************************************************* {COPYRIGHT-END} ****/}}
{{- $compName := "cem-users" -}}
{{- include "sch.config.init" (list . "cem.sch.chart.config.values") -}}
{{- $deploymentName := include "sch.names.fullCompName" (list . $compName) -}}
{{- $cdbConfigTemplateName := include "sch.names.volumeClaimTemplateName" (list . "config" $deploymentName) -}}
{{- $rootData := fromYaml (include "root.data" .) -}}
{{- $rootMetering := $rootData.metering -}}
{{- $configMapName := include "sch.names.fullCompName" (list . $compName) -}}
{{- $serviceAccountName := include "sch.names.fullCompName" (list . $compName) -}}
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ $deploymentName }}
namespace: {{ .Release.Namespace }}
labels:
{{ include "sch.metadata.labels.standard" (list . $compName) | indent 4 }}
origin: helm-cem
spec:
replicas: {{ .Values.cemusers.clusterSize }}
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ include "sch.names.appName" (list .) | quote }}
component: {{ $compName | quote }}
template:
metadata:
labels:
{{ include "sch.metadata.labels.standard" (list . $compName) | indent 8 }}
origin: helm-cem
annotations:
checksum/cemusers-config: {{ include (print $.Template.BasePath "/config/cem-users-config.yaml") . | sha256sum }}
{{- include "sch.metadata.annotations.metering" (list . $rootMetering) | indent 8 }}
spec:
{{ include "ingress-host-alias" . | indent 6 }}
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
#If you specify multiple nodeSelectorTerms associated with nodeAffinity types,
#then the pod can be scheduled onto a node if one of the nodeSelectorTerms is satisfied.
#
#If you specify multiple matchExpressions associated with nodeSelectorTerms,
#then the pod can be scheduled onto a node only if all matchExpressions can be satisfied.
#
#valid operators: In, NotIn, Exists, DoesNotExist, Gt, Lt
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
{{- if .Values.arch }}
- {{ .Values.arch }}
{{- else }}
- {{ template "arch" . }}
{{- end }}
initContainers:
- name: create-random-secrets
image: "{{ .Values.global.image.repository }}/hdm-cem-users:{{ .Values.commonimages.cemusers.image.tag }}"
command: ["node"]
args: ["create-secrets.js"]
env:
- name: RELEASE
value: '{{ template "releasename" . }}'
- name: waitforcouchdb
image: "{{ .Values.global.image.repository }}/hdm-cem-users:{{ .Values.commonimages.cemusers.image.tag }}"
command: ["sh", "-c", "i=1;until getent hosts {{ template "releasename" . }}-couchdb.{{ .Release.Namespace }}.svc; do echo waiting for couchdb $i;i=$((i+1)); sleep 2; done;"]
- name: waitforredis
image: "{{ .Values.global.image.repository }}/hdm-cem-users:{{ .Values.commonimages.cemusers.image.tag }}"
command: ["sh", "-c", "i=1;until getent hosts {{ template "releasename" . }}-redis-master-svc.{{ .Release.Namespace }}.svc; do echo waiting for redis $i;i=$((i+1)); sleep 2; done;"]
containers:
- name: cem-users
image: "{{ .Values.global.image.repository }}/hdm-cem-users:{{ .Values.commonimages.cemusers.image.tag }}"
ports:
- containerPort: 6002
protocol: TCP
livenessProbe:
tcpSocket:
port: 6002
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 20
readinessProbe:
tcpSocket:
port: 6002
initialDelaySeconds: 20
timeoutSeconds: 20
env:
- name: LICENSE
value: {{ .Values.license | default "not accepted" }}
- name: ENV_ICP
value: "1"
- name: PORT
value: "6002"
- name: BASEURL
value: '{{ include "cem.services.cemusers" . }}'
{{ include "cloudeventmanagement.cemusers.env" . | indent 8 }}
- name: VCAP_APPLICATION
value: '{}'
- name: INGRESS_PREFIX
value: '{{ .Values.global.ingress.prefix }}'
- name: INGRESS_DOMAIN
value: '{{ .Values.global.ingress.domain }}'
resources:
{{ include "ibmcemprod.comp.size.data" (list . "cemusers" "resources") | indent 10 }}
terminationMessagePath: "/dev/termination-log"
imagePullPolicy: IfNotPresent
volumeMounts:
- name: {{ $cdbConfigTemplateName }}
mountPath: /etc/oidc
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
securityContext:
runAsUser: 1000
serviceAccountName: {{ $serviceAccountName }}
volumes:
- name: {{ $cdbConfigTemplateName }}
configMap:
name: {{ $configMapName }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
After the Helm chart is successfully deployed, a cluster administrator can run the following kubectl
command to execute the registration logic and obtain the secret:
kubectl exec -n {{ .Release.Namespace }} -t `kubectl get pods -l release={{ .Release.Name }} -n {{ .Release.Namespace }} | grep "{{ .Release.Name }}-ibm-cem-cem-users" | grep "Running" | head -n 1 | awk '{print $1}'` bash -- "/etc/oidc/oidc_reg.sh" "`echo $(kubectl get secret platform-oidc-credentials -o "jsonpath={.data.OAUTH2_CLIENT_REGISTRATION_SECRET}")`"