Red Hat CodeReady Containers(OpenShift4)にRH-SSOをデプロイする

# はじめに
OpenShiftの勉強中に調べて、残しておきたいと思ったものを記事にしました。
「CodeReady Containers」でRH-SSOをデプロイした際の作業内容です。
デプロイ手順は、『[OpenShift Interactive Learning Playground](https://learn.openshift.com/playgrounds/)』でも試す事ができたので、手元に実機環境がない場合でもRH-SSOのお勉強ができます。

# 環境
- macOS Catalina v10.15.6
- CodeReady Containers v1.15.0+e317bed

# RH-SSOのデプロイ
ocコマンドを利用できるようにし、kubeadminでログインします。

```
% eval $(./crc oc-env)
% oc login -u kubeadmin -p ILWgF-VfgcQ-p6mJ4-Jztez https://api.crc.testing:6443
```

利用できるtemplateを確認します。

```
% oc get template -n openshift -o name
template.template.openshift.io/3scale-gateway
template.template.openshift.io/amq63-basic
template.template.openshift.io/amq63-persistent
template.template.openshift.io/amq63-persistent-ssl
template.template.openshift.io/amq63-ssl
template.template.openshift.io/apicurito
template.template.openshift.io/cache-service
template.template.openshift.io/cakephp-mysql-example
template.template.openshift.io/cakephp-mysql-persistent
template.template.openshift.io/dancer-mysql-example
template.template.openshift.io/dancer-mysql-persistent
template.template.openshift.io/datagrid-service
template.template.openshift.io/datavirt64-basic-s2i
template.template.openshift.io/datavirt64-extensions-support-s2i
template.template.openshift.io/datavirt64-ldap-s2i
template.template.openshift.io/datavirt64-secure-s2i
template.template.openshift.io/decisionserver64-amq-s2i
template.template.openshift.io/decisionserver64-basic-s2i
template.template.openshift.io/django-psql-example
template.template.openshift.io/django-psql-persistent
template.template.openshift.io/dotnet-example
template.template.openshift.io/dotnet-pgsql-persistent
template.template.openshift.io/eap-cd-basic-s2i
template.template.openshift.io/eap-cd-starter-s2i
template.template.openshift.io/eap72-basic-s2i
template.template.openshift.io/eap72-https-s2i
template.template.openshift.io/eap72-mongodb-persistent-s2i
template.template.openshift.io/eap72-mongodb-s2i
template.template.openshift.io/eap72-mysql-persistent-s2i
template.template.openshift.io/eap72-mysql-s2i
template.template.openshift.io/eap72-postgresql-persistent-s2i
template.template.openshift.io/eap72-postgresql-s2i
template.template.openshift.io/eap72-sso-s2i
template.template.openshift.io/eap72-third-party-db-s2i
template.template.openshift.io/fuse75-console
template.template.openshift.io/fuse76-console
template.template.openshift.io/httpd-example
template.template.openshift.io/jenkins-ephemeral
template.template.openshift.io/jenkins-ephemeral-monitored
template.template.openshift.io/jenkins-persistent
template.template.openshift.io/jenkins-persistent-monitored
template.template.openshift.io/jws31-tomcat7-basic-s2i
template.template.openshift.io/jws31-tomcat7-https-s2i
template.template.openshift.io/jws31-tomcat7-mongodb-persistent-s2i
template.template.openshift.io/jws31-tomcat7-mongodb-s2i
template.template.openshift.io/jws31-tomcat7-mysql-persistent-s2i
template.template.openshift.io/jws31-tomcat7-mysql-s2i
template.template.openshift.io/jws31-tomcat7-postgresql-persistent-s2i
template.template.openshift.io/jws31-tomcat7-postgresql-s2i
template.template.openshift.io/jws31-tomcat8-basic-s2i
template.template.openshift.io/jws31-tomcat8-https-s2i
template.template.openshift.io/jws31-tomcat8-mongodb-persistent-s2i
template.template.openshift.io/jws31-tomcat8-mongodb-s2i
template.template.openshift.io/jws31-tomcat8-mysql-persistent-s2i
template.template.openshift.io/jws31-tomcat8-mysql-s2i
template.template.openshift.io/jws31-tomcat8-postgresql-persistent-s2i
template.template.openshift.io/jws50-tomcat9-basic-s2i
template.template.openshift.io/jws50-tomcat9-https-s2i
template.template.openshift.io/jws50-tomcat9-mongodb-persistent-s2i
template.template.openshift.io/jws50-tomcat9-mongodb-s2i
template.template.openshift.io/jws50-tomcat9-mysql-persistent-s2i
template.template.openshift.io/jws50-tomcat9-mysql-s2i
template.template.openshift.io/jws50-tomcat9-postgresql-persistent-s2i
template.template.openshift.io/mariadb-ephemeral
template.template.openshift.io/mariadb-persistent
template.template.openshift.io/mongodb-ephemeral
template.template.openshift.io/mongodb-persistent
template.template.openshift.io/mysql-ephemeral
template.template.openshift.io/mysql-persistent
template.template.openshift.io/nginx-example
template.template.openshift.io/nodejs-mongo-persistent
template.template.openshift.io/nodejs-mongodb-example
template.template.openshift.io/openjdk-web-basic-s2i
template.template.openshift.io/postgresql-ephemeral
template.template.openshift.io/postgresql-persistent
template.template.openshift.io/processserver64-amq-mysql-persistent-s2i
template.template.openshift.io/processserver64-amq-mysql-s2i
template.template.openshift.io/processserver64-amq-postgresql-persistent-s2i
template.template.openshift.io/processserver64-amq-postgresql-s2i
template.template.openshift.io/processserver64-basic-s2i
template.template.openshift.io/processserver64-externaldb-s2i
template.template.openshift.io/processserver64-mysql-persistent-s2i
template.template.openshift.io/processserver64-mysql-s2i
template.template.openshift.io/processserver64-postgresql-persistent-s2i
template.template.openshift.io/rails-pgsql-persistent
template.template.openshift.io/rails-postgresql-example
template.template.openshift.io/redis-ephemeral
template.template.openshift.io/redis-persistent
template.template.openshift.io/rhdm77-authoring
template.template.openshift.io/rhdm77-authoring-ha
template.template.openshift.io/rhdm77-kieserver
template.template.openshift.io/rhdm77-prod-immutable-kieserver
template.template.openshift.io/rhdm77-prod-immutable-kieserver-amq
template.template.openshift.io/rhdm77-trial-ephemeral
template.template.openshift.io/rhpam77-authoring
template.template.openshift.io/rhpam77-authoring-ha
template.template.openshift.io/rhpam77-kieserver-externaldb
template.template.openshift.io/rhpam77-kieserver-mysql
template.template.openshift.io/rhpam77-kieserver-postgresql
template.template.openshift.io/rhpam77-managed
template.template.openshift.io/rhpam77-prod
template.template.openshift.io/rhpam77-prod-immutable-kieserver
template.template.openshift.io/rhpam77-prod-immutable-kieserver-amq
template.template.openshift.io/rhpam77-prod-immutable-monitor
template.template.openshift.io/rhpam77-trial-ephemeral
template.template.openshift.io/s2i-fuse75-spring-boot-camel
template.template.openshift.io/s2i-fuse75-spring-boot-camel-rest-3scale
template.template.openshift.io/s2i-fuse75-spring-boot-camel-xml
template.template.openshift.io/s2i-fuse76-spring-boot-camel
template.template.openshift.io/s2i-fuse76-spring-boot-camel-rest-3scale
template.template.openshift.io/s2i-fuse76-spring-boot-camel-xml
template.template.openshift.io/sso72-https
template.template.openshift.io/sso72-mysql
template.template.openshift.io/sso72-mysql-persistent
template.template.openshift.io/sso72-postgresql
template.template.openshift.io/sso72-postgresql-persistent
template.template.openshift.io/sso73-https
template.template.openshift.io/sso73-mysql
template.template.openshift.io/sso73-mysql-persistent
template.template.openshift.io/sso73-ocp4-x509-https
template.template.openshift.io/sso73-ocp4-x509-mysql-persistent
template.template.openshift.io/sso73-ocp4-x509-postgresql-persistent
template.template.openshift.io/sso73-postgresql
template.template.openshift.io/sso73-postgresql-persistent
template.template.openshift.io/sso74-https
template.template.openshift.io/sso74-ocp4-x509-https
template.template.openshift.io/sso74-ocp4-x509-postgresql-persistent
template.template.openshift.io/sso74-postgresql
template.template.openshift.io/sso74-postgresql-persistent
```

ssoは下部に並んでいるので見やすいですね...「sso74-ocp4-x509-https」あたりが無難そうで良いかなぁ？
デプロイ先として「sso」というプロジェクトを作成します。

```
% oc new-project sso
Now using project "sso" on server "https://api.crc.testing:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app ruby~https://github.com/sclorg/ruby-ex.git

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node

```

defaultサービスアカウントにviewロールを付与するらしいです。

```
% oc policy add-role-to-user view system:serviceaccount:$(oc project -q):default
clusterrole.rbac.authorization.k8s.io/view added: "system:serviceaccount:sso:default"
```

テンプレートを指定してRH-SSOをデプロイします。
ここでは、「sso74-ocp4-x509-https」を使用します。管理者アカウントを任意のものにしたいので、以下のパラメータオプションを指定します。
SSO_ADMIN_USERNAME
SSO_ADMIN_PASSWORD
指定しないと、ランダムに作成されます。作成したユーザ名とパスワードは画面上に表示されます。

```
% oc new-app --template=sso74-ocp4-x509-https -p SSO_ADMIN_USERNAME=sso_admin -p SSO_ADMIN_PASSWORD=sso_password
--> Deploying template "openshift/sso74-ocp4-x509-https" to project sso

     Red Hat Single Sign-On 7.4 on OpenJDK (Ephemeral)
     ---------
     An example application based on RH-SSO 7.4 on OpenJDK image. For more information about using this template, see https://github.com/jboss-container-images/redhat-sso-7-openshift-image/tree/sso74-dev/docs.

     A new RH-SSO service has been created in your project. The admin username/password for accessing the master realm via the RH-SSO console is sso_admin/sso_password. The HTTPS keystore used for serving secure content, the JGroups keystore used for securing JGroups communications, and server truststore used for securing RH-SSO requests were automatically created via OpenShift's service serving x509 certificate secrets.

     * With parameters:
        * Application Name=sso
        * Custom RH-SSO Server Hostname=
        * JGroups Cluster Password=Qt08VMWTx47A8B2SlTJwlgBMdniWoDUj # generated
        * Datasource Minimum Pool Size=
        * Datasource Maximum Pool Size=
        * Datasource Transaction Isolation=
        * ImageStream Namespace=openshift
        * RH-SSO Administrator Username=sso_admin
        * RH-SSO Administrator Password=sso_password
        * RH-SSO Realm=
        * RH-SSO Service Username=
        * RH-SSO Service Password=
        * Container Memory Limit=1Gi

--> Creating resources ...
    configmap "sso-service-ca" created
    service "sso" created
    service "sso-ping" created
    route.route.openshift.io "sso" created
    deploymentconfig.apps.openshift.io "sso" created
--> Success
    Access your application via route 'sso-sso.apps-crc.testing' 
    Run 'oc status' to view your app.
```

oc get pod でSSOのPod(-deployではない方)「1/1 Running」になるまで待ちます。※結構時間がかかるので「-w」で監視状態にしておくとよいです。

```
% oc get pod -w
NAME           READY   STATUS      RESTARTS   AGE
sso-1-deploy   0/1     Completed   0          2m16s
sso-1-m4fkj    1/1     Running     0          2m6s
```

routeで生成されたURLにブラウザでアクセスすると、RH-SSOの画面が表示されます。

```
% oc get svc,route
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/sso        ClusterIP   172.25.202.12   <none>        8443/TCP   3m49s
service/sso-ping   ClusterIP   None            <none>        8888/TCP   3m49s

NAME                           HOST/PORT                  PATH   SERVICES   PORT    TERMINATION   WILDCARD
route.route.openshift.io/sso   sso-sso.apps-crc.testing          sso        <all>   reencrypt     None
```

上記の場合だと、「[https://sso-sso.apps-crc.testing/](https://sso-sso.apps-crc.testing/)」です。
![スクリーンショット 2020-09-03 9.25.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/8e2f68c6-2caf-902f-bcd0-12ebefb12104.png)

「Administration Console」を開くと、ログイン画面が出るので、デプロイ時に定義した「sso_admin」でログインします。
![スクリーンショット 2020-09-03 9.26.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/bc80f2d0-46ab-5a86-1263-c46b2ad6f7d4.png)

「RH-SSO Admin Console」の画面です。
![スクリーンショット 2020-09-03 9.26.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/1cf376b5-42f9-a20a-da2f-ef388ed9e68f.png)

# REST APIを試す
GUIだけでなく、CLIでも確認します。REST APIの仕様書は以下となるようです。（RH-SSOのマニュアルから辿れます）
https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-7.4/restapi/

接続先は環境にも依りますが、OpenShiftではrouteに記載されているホスト名＋ポート番号です。

アクセスTOKENの取得をします。
TOKENの有効期間は1分間のようなので、シェルスクリプトで使うのであれば毎回取得した方が良いかもしれません。

```
% curl -s -d "client_id=admin-cli" -d "username=sso_admin" -d "password=sso_password" -d "grant_type=password" https://sso-sso.apps-crc.testing/auth/realms/master/protocol/openid-connect/token --insecure | jq -r .access_token
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2MVNEbmhQV3VOMVIycHhKaWJVb0RNaHAtYzN5bUxucS1VaDJqN1NGMno0In0.eyJleHAiOjE1OTkwOTI5MzEsImlhdCI6MTU5OTA5Mjg3MSwianRpIjoiNzI3MTI2OWQtOGYzMC00NzU3LWFiZjgtMDIzNTUwNWYzYjJjIiwiaXNzIjoiaHR0cHM6Ly9zc28tc3NvLmFwcHMtY3JjLnRlc3RpbmcvYXV0aC9yZWFsbXMvbWFzdGVyIiwic3ViIjoiYjU0YzE3MGItZTZlMi00NjBmLWIxMWQtNjdiMWIzOTcyNDFjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2Vzc2lvbl9zdGF0ZSI6Ijk2M2M5MTM2LTRjYzAtNDU0OS1iMzNiLTdlNzI3ZDI3MTIyZiIsImFjciI6IjEiLCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNzb19hZG1pbiJ9.p4cRz0CG5Af_uN53kS1ZzIQAWECoORpg0zZzs3ncqCNgVJoel63SuuRWc_t16n9drqYsmAuszf_oguaDQAyleA8YMNnSi4OEk9fpYfAauAx9eLjQUGnsEuntdrNfA7cOuYXIjJ9ZAEv-9SxU9BphpKgQ1xsyDKzzoBFPOBDzKfyzvM2T5d7snotsZ6zem_-bLGgD5fE693lqt6BgLpezNt4JJGLbrdSkVAMoBn4QKPDe2IJB8YbgfkZngSUJa0TBDHXo8NWSx4fYeXIypSI6KGhhOQNK1oD1UXx9xBic5DZs6fBenfUwHgtlYbfNpOuaQM8JvL2e2N_0v3GgGvQGCA
```


■Get clients belonging to the realm Returns a list of clients belonging to the realm
GET /{realm}/clients

最終的に、Client-IDとIDをJSONで整形して出力させています。

```
% TOKEN=`curl -s -d "client_id=admin-cli" -d "username=sso_admin" -d "password=sso_password" -d "grant_type=password" https://sso-sso.apps-crc.testing/auth/realms/master/protocol/openid-connect/token --insecure | jq -r .access_token` &&\
 curl -s -H "Authorization: bearer $TOKEN" https://sso-sso.apps-crc.testing/auth/admin/realms/master/clients --insecure | jq '[.[] | {(.clientId): .id}] | add' 
{
  "account": "e6cfa122-e0d0-4671-bef7-a24d3440bbef",
  "account-console": "1f12b035-bbbb-4baa-8914-3b259bd3876b",
  "admin-cli": "47bb0cd4-f07f-4ddf-9667-97f32804f3e3",
  "broker": "4e1135b2-389b-474e-a123-2ca42b064f74",
  "master-realm": "4f5dec06-ee4c-46ba-9b38-e37db9b3c753",
  "security-admin-console": "eb13dafb-9ac5-4eb6-a73b-d43ec8aa084f"
}
```

■Get user sessions for client Returns a list of user sessions associated with this client
GET /{realm}/clients/{id}/user-sessions

元々このREST APIを試したかったのですが、{id}の説明が「id of client (not client-id)」となっており、色々調べて上述で取得した値と分かりました。

```
% TOKEN=`curl -s -d "client_id=admin-cli" -d "username=sso_admin" -d "password=sso_password" -d "grant_type=password" https://sso-sso.apps-crc.testing/auth/realms/master/protocol/openid-connect/token --insecure | jq -r .access_token` &&\
 ID=`curl -s -H "Authorization: bearer $TOKEN" https://sso-sso.apps-crc.testing/auth/admin/realms/master/clients --insecure | jq '[.[] | {(.clientId): .id}] | add' | jq -r '."security-admin-console"'` &&\
 curl -s -H "Authorization: bearer $TOKEN" https://sso-sso.apps-crc.testing/auth/admin/realms/master/clients/$ID/user-sessions --insecure | jq .
[
  {
    "id": "d0aca740-649d-4153-be21-e875e43b0a99",
    "username": "sso_admin",
    "userId": "b54c170b-e6e2-460f-b11d-67b1b397241c",
    "ipAddress": "192.168.64.1",
    "start": 1599101316000,
    "lastAccess": 1599101316000,
    "clients": {
      "eb13dafb-9ac5-4eb6-a73b-d43ec8aa084f": "security-admin-console"
    }
  }
]
```

「RH-SSO Admin Console」の画面と同じ結果が採取できています。
![スクリーンショット 2020-09-03 12.02.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/c1149511-c643-89bf-3f97-699621097bfb.png)

# おわりに
マニュアルを見ながら試してますが、色々と惑わされる情報があるので、できたパターンを残しておかないと忘れてしまいますね...歳ですか。

マニュアルは以下です。
https://access.redhat.com/documentation/ja-jp/red_hat_single_sign-on/7.4/html-single/red_hat_single_sign-on_for_openshift_on_openjdk/index#Example-Deploying-SSO