rh_sso_multi_realm
:scrollbar: :data-uri: :toc2: :linkattrs:
= rh-sso-multi-realm
:numbered:
== Overview
This role is intended to provision a persistent Red Hat Single Sign-On (RH-SSO) installation on Red Hat's OpenShift Container Platform (OCP).
It also allows for management (ie: creation and deletion) of a configurable number of SSO realms in that RH-SSO installation.
This role might be valuable in the following circumstances :
. Instructor Led Training (ILTs), Hackathons and workshops: + Given X number of students in an ILT requiring RH-SSO, provision a single central multi-realm RH-SSO where each student is assigned their own realm. + The student is provided with administrative credentials to their assigned realm. + This approach might be more desirable than the alternative where each student provisions their own RH-SSO.
. RH-SSO enablement + A few learning objectives might be:
.. Demonstrate the provisioning of RH-SSO on OCP. + In particular, the RH-SSO variant that leverages OCP's link:https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.2/html-single/red_hat_single_sign-on_for_openshift/index#what_is_red_hat_single_sign_on[service serving x509 certificate secrets].
.. Integration with an external smtp provider to send out emails and facilitate a user self-registration workflow. .. Invocation of the REST Admin API of RH-SSO using OAuth2 access and refresh tokens.
NOTE: This Ansible role does not yet make use of the link:https://github.com/keycloak/keycloak-operator[Keycloak-operator]. Primarily because of inability to configure health checks and limits and requests in the current implementation of the operator as described link:https://keycloak.discourse.group/t/various-issues-with-keycloak-operator/845[here]. The list of currently open jiras related to the operator is link:https://issues.redhat.com/browse/KEYCLOAK-13065?jql=project%20%3D%20KEYCLOAK%20AND%20component%20%3D%20%22Containers%20-%20Operator%22[here].
=== Reference
. link:https://www.keycloak.org/documentation.html[Latest keycloak documentation] . link:https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/7.2/html-single/red_hat_single_sign-on_for_openshift[RH-SSO for OCP] . link:https://docs.openshift.com/container-platform/3.10/dev_guide/secrets.html#service-serving-certificate-secrets[OCP Service Serving Certificate Secrets]
=== Assumptions and Pre-reqs . Ensure that ansible is installed on your local machine. + The version of ansible that this ansible role has been most recently tested on is: 2.6.5-1 on Fedora 29.
. This ansible role assumes the existence of a remote OCP cluster with a minimum of 6GB RAM and 2 CPUs. + The version of OCP that this ansible role has been most recently tested on is: 3.11.43.
. This Ansible role makes heavy use of an oc
client running on your local machine.
Ensure that this oc
client is on the $PATH of your local environment and its version aligns with that of your OCP environment.
. [blue]#The local oc client will need to already be authenticated to the remote OCP environment as a cluster-admin user.#
==== Wildcard Certificate
This ansible role assumes that your pre-existing OCP cluster is configured with a certificate signed by a legitimate Certificate Authority (such as LetsEncrypt).
NOTE: link:https://github.com/redhat-gpe/mw_docs/blob/master/ocp_cluster_workshop.adoc[OCP cluster workshops] aquired from Red Hat Partner Demo System (RHPDS) and Red Hat Open Partner Enablement Network (OPEN) come equipped with LetsEncrypt certificates. You can skip this section.
A very nice tutorial on acquiring a LetsEncrypt wildcard certificate and applying it to your OCP cluster can be found here:
Hey Tate: How to purchase a domain name for valid SSL certs in your development systems. ** https://youtu.be/sBcDnQj9_QM ** https://docs.google.com/document/d/1APb8DF9JkbuGA3FaI8cXdSeFU0FsW93auUofPj6n4zo/edit?usp=sharing
Hey Tate: Create valid wildcard SSL certificate for your domain for free with certbot! ** https://youtu.be/3D4-MWG1Bew ** https://docs.google.com/document/d/1m8FFTybu5VIYkRlWvqqFBsIO3a9GznbLkxZyyEI4vto/edit?usp=sharing
Hey Tate: Running OpenShift and routes locally with valid SSL certificate. ** https://youtu.be/JvU5BWE5pS8 ** https://docs.google.com/document/d/1hk3yacNkDWHdcSJb7SOl_9wblVq_hvwlaoVK35P-EVQ/edit?usp=sharing
An alternative to a wildcard certificate for your entire OCP cluster could simply be to link:https://developers.redhat.com/blog/2019/02/06/using-a-public-certificate-with-red-hat-single-sign-on-keycloak/[secure the RH-SSO route with a LetsEncrypt] certificate.
== RH-SSO Deployment
=== Environment variables
The following environment variables will need to be set in the shell of your local environment from where you will execute ansible. These environment variables will be used throughout this role.
Update each of the following and then execute:
echo "export OCP_PROJECT_PREFIX=
Execute the following:
echo "export SSO_SITE_ADMIN_USERNAME=master" >> ~/.bashrc echo "export SSO_SITE_ADMIN_PASSWORD=master" >> ~/.bashrc echo "export rhsso_project=rhsso-$ocp_sso_admin_id" >> ~/.bashrc source ~/.bashrc
OCP wildcard DNS after "apps"; ie; 2345.openshift.opentlc.com
examples:
Code Ready Containers: SUBDOMAIN_BASE=crc.testing
ravello vm : SUBDOMAIN_BASE=oc whoami --show-server | cut -d'-' -f 2 | cut -d':' -f 1
ocp workshop : SUBDOMAIN_BASE=oc whoami --show-server | cut -d'.' -f 2,3,4,5 | cut -d':' -f 1
echo "export SUBDOMAIN_BASE=oc whoami --show-server | cut -d'.' -f 2,3,4,5 | cut -d':' -f 1
" >> ~/.bashrc
source ~/.bashrc
=== Ansible Set-up
. Install this role: +
$ ansible-galaxy install gpe_mw_ansible.rh_sso_multi_realm --force -p /etc/ansible/roles
- Alternatively, you can also installing the role locally likewise:
$ ansible-galaxy install gpe_mw_ansible.rh_sso_multi_realm --force -p $HOME/.ansible/roles
. Create Playbook: +
$ echo "
- hosts: all
become: false
gather_facts: False
vars_files:
roles:
- gpe_mw_ansible.rh_sso_multi_realm " > /tmp/rh_sso_multi_realm.yml
=== Provision RH-SSO
The OCP namespace for the RH-SSO multi-realm application will be owned by the following user: {{ocp_sso_admin_id}}. A user called {{ocp_sso_admin_id}} will be assigned a clusterquota so as to manage limits and requests assigned to 3scale
. Ensure ImageStream is in openshift
namespace:
.. redhat-sso73-openshift image stream.
+
Execute the following if this image stream does not exist in the openshift
namespace:
+
$ oc create -f https://raw.githubusercontent.com/jboss-container-images/redhat-sso-7-openshift-image/v7.4.0.GA/templates/sso74-image-stream.json -n openshift
. Execute: +
$ ansible-playbook -i localhost, -c local /tmp/rh_sso_multi_realm.yml
-e"ocp_user_needs_quota=true"
-e"ACTION=create"
-e"SSO_SITE_ADMIN_USERNAME=$SSO_SITE_ADMIN_USERNAME"
-e"SSO_SITE_ADMIN_PASSWORD=$SSO_SITE_ADMIN_PASSWORD"
-e"admin_username=$ocp_sso_admin_id"
-e"subdomain_base=$SUBDOMAIN_BASE"
. Set an environment variable that references the URL of the newly provisioned RH-SSO: +
echo "export rhsso_hostname=$(oc get route sso -n rhsso-$ocp_sso_admin_id --template "{{.spec.host}}" -n rhsso-$ocp_sso_admin_id)" >> ~/.bashrc
source ~/.bashrc
. Once provisioning has completed, view the certificate associated with the new RH-SSO server: +
$ echo '' | openssl s_client -connect oc get route sso -n $rhsso_project --template "{{.spec.host}}"
:443 | more
- Assuming your OCP cluster was provisioned using a wildcard certificate signed by the LetsEncrypt Certificate Authority, the response should include the following :
... subject=CN = master.3295.openshift.opentlc.com
issuer=C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 ...
. If OCP cluster has been provisioned using LetsEncrypt (or some other legitimate) Certificate Authority and has been issued a wildcard certificate, you can view the details as follows: +
$ curl -v -X GET "https://$rhsso_hostname/auth/realms/master/.well-known/openid-configuration" | python -m json.tool
...
- subjectAltName: host "sso-rhsso-sso0.apps.3295.openshift.opentlc.com" matched cert's "*.apps.3295.openshift.opentlc.com"
=== RH-SSO Admin Console
. Open a web browser and navigate to the console of the master
realm :
+
$ echo -en "\nhttps://$rhsso_hostname/auth/admin/master/console\n\n"
. Authenticate using the values of the $SSO_SITE_ADMIN_USERNAME and $SSO_SITE_ADMIN_PASSWORD environment variables used when the RH-SSO instance was provisioned.
. As the RH-SSO site admin, you have full access to all of its resources. + image::images/master_homepage.png[]
=== Delete RH-SSO
$ ansible-playbook -i localhost, -c local /tmp/rh_sso_multi_realm.yml
-e"ACTION=remove"
-e"subdomain_base=$SUBDOMAIN_BASE"
[[realm_mgmt]] == Realm Creation / Deletion
RH-SSO allows for multiple realms to be created. Each realm is fully autonomous from all other realms.
This section of the install assists in the automated creation of realms for various use-cases.
==== SMTP Providers
It's likely that you will want the realms of your RH-SSO to have the ability to send emails.
As an example, the generic realms
configured as part of this ansible role are configured for a user sign-up flow that requires the new user to verify signup via a link provided in an email.
In RH-SSO, smtp settings are configured at within the scope of a realm. When provisioning realms, you can specify the following ansible variables:
- smtp_host
- smtp_userid
- smtp_passwd
A few SMTP providers with Free Plans that this ansible role has been tested with are listed below:
. SocketLabs: Currently offering a free plan that allows for link:https://www.socketlabs.com/signup/[2000 emails per month] . SendGrid: Currently offering a free plan that allows for link:https://sendgrid.com/pricing/[100 emails per day]
=== Generic Realms
Via this ansible, a configurable number of SSO realms can be created based on the value of the following variables: first_generic_realm
and last_generic_realm
.
If the value of last_generic_realm
is less than 1, then generic realms will not be created.
The name of these generic realms can be customized by over-riding the ansible variable: realm_base_name.
Each of these SSO realms are enabled to allow one or more users to register as a user of this realm. The default behavior of registered realm users is that they are full administrators of the realm. Obviously this behavior is appropriate for demo and learning scenarios only.
=== KIE Realm If when creating realms the variable loadKieRealm is set to true, then a special realm specific to supporting Business and Decision Manager scenarios will be created.
. Details of this provisioned kieRealm are as follows:
.. realmId: kieRealm .. Login URL: https://$rhsso_hostname/auth/admin/kieRealm/console .. Realm Admin User Id: ssoRealmAdmin .. Realm Admin Passwd: pam .. KIE related roles: ... admin ... kie-server ... kiemgmt ... rest-all
. Details of all users registered with this realm can be identified as follows: +
$ cat templates/kie-realm.json | jq .users
=== CRW Realm If when creating realms the variable loadCRWRealm is set to true, then a special realm specific to supporting Code Ready Workspaces will be created.
Details of this provisioned crwRealm
. realmId: crwRealm . Login URL: https://$rhsso_hostname/auth/admin/crwRealm/console . Admin User Id: admin . Admin Passwd: admin
=== OpenShift Realm
If when creating realms the variable loadOCPRealm is set to true, then a realm called ocp-realm
will be created in your RH-SSO.
Its purpose is to serve as an link:https://access.redhat.com/documentation/en-us/red_hat_jboss_middleware_for_openshift/3/html/red_hat_single_sign-on_for_openshift/tutorials#OSE-SSO-AUTH-TUTE[IdentityProvider for authentication to OCP] .
Details of this provisioned ocp-realm are as follows:
. realmId: ocp-realm
. Login URL: https://$rhsso_hostname/auth/admin/ocp-realm/console
. SSO Client: ocp-client
. Admin User Id: gpte-mgr
. Admin Passwd: r3dh4t1!
. SSO users: Configurable number of users associated with this realm are created as per the variables: start_ocp_user
and end_ocp_user
+
The name of each user of this realm will start with the value: {{ocp_realm_user_base_name}} (default = ocp
)
. OCP related roles: + None. Roles must be assigned by a cluster admin .
After provisioning of the ocp-realm
in RH-SSO, there are some additional steps to execute in the master node of OCP.
Details regarding these steps are detailed in : {{new_app_output_dir}}/ocp-realm-suggestion.txt
In the IdentityProvider section of the master-config.xml
file, remember to override the htpasswd section with the ocp-client settings.
=== Create Realms
. Set the following environment variables in your shell and then execute the ansible command as listed below: +
smtp_host= # SMTP provider details allow RH-SSO to deliver emails in support of functionality such as User Registration smtp_userid= smtp_passwd=
FIRST_GENERIC_REALM=1 # first realm to be initialized will be: realm$FIRST_GENERIC_REALM LAST_GENERIC_REALM=1 # last realm to be initialized will be : realm$LAST_GENERIC_REALM ; if value is less than 1, then generic realms will not be created realm_base_name=realm # base name of your generic realms. Default is simply: realm.
loadKieRealm=false # default is false. If true, then will initialize a realm utilized to support Red Hat Process Automation and Decision Manager installations
loadCRWRealm=false # default is false. If true, then a realm will be initialized to support Red Hat Code Ready Workspaces
crw_redirect_url="" # Only applicable if loadCRWRealm=true. Set to URL of CodeReadyWorkspace route.
loadOCPRealm=true # default is false. If true, then will configure a realm called ocp-realm that can be used to configure OCP to leverage RH-SSO for authentication end_ocp_user=1 # last user to be created in ocp-realm will be: user$end_ocp_user
$ ansible-playbook -i localhost, -c local /tmp/rh_sso_multi_realm.yml
-e"ACTION=realm_mgmt"
-e"create_realms=true"
-e"subdomain_base=$SUBDOMAIN_BASE"
-e"smtp_host=$smtp_host"
-e"smtp_passwd=$smtp_passwd"
-e"smtp_userid=$smtp_userid"
-e"SSO_SITE_ADMIN_USERNAME=$SSO_SITE_ADMIN_USERNAME"
-e"SSO_SITE_ADMIN_PASSWORD=$SSO_SITE_ADMIN_PASSWORD"
-e"admin_username=$ocp_sso_admin_id"
-e"first_generic_realm=$FIRST_GENERIC_REALM"
-e"last_generic_realm=$LAST_GENERIC_REALM"
-e"realm_base_name=$realm_base_name"
-e"loadKieRealm=$loadKieRealm"
-e"loadCRWRealm=$loadCRWRealm"
-e"crw_redirect_url=$crw_redirect_url"
-e"loadOCPRealm=$loadOCPRealm"
-e"end_ocp_user=$end_ocp_user"
-e"rhsso_hostname=$rhsso_hostname"
. At the conclusion of the ansible execution, console messages such as the following should be written: +
rh-sso-multi-realm : Realm Rollout Complete]
ok: [localhost] => { "msg": [ "create_realms: true", "new_app_output_dir: /home/jbride/provisioning_output/3295.openshift.opentlc.com", "start and end realms = 1 25" ] }
- The json representation of each realm created can be found in the directory mentioned as: new_app_output_dir.
==== Delete Realms
$ ansible-playbook -i localhost, -c local /tmp/rh_sso_multi_realm.yml
-e"ACTION=realm_mgmt"
-e"first_generic_realm=$FIRST_GENERIC_REALM"
-e"last_generic_realm=$LAST_GENERIC_REALM"
-e"subdomain_base=$SUBDOMAIN_BASE"
-e"SSO_SITE_ADMIN_USERNAME=$SSO_SITE_ADMIN_USERNAME"
-e"SSO_SITE_ADMIN_PASSWORD=$SSO_SITE_ADMIN_PASSWORD"
-e"admin_username=$ocp_sso_admin_id"
-e"rhsso_hostname=$rhsso_hostname"
-e"create_realms=false"
== Generic Realms: User Registration
This section of the lab assumes you have already provisioned one or more generic SSO realms as per the <
The purpose of this section is to provide students with instructions that detail how to register as a user of a previously provisioned generic SSO realm. This entire section can be copy-n-pasted into the lab instructions of a course that leverages this multi-realm RH-SSO.
. Set an environment variable that corresponds to a specific realm (eg.
$ echo "export rhsso_realm=
$ source ~/.bashrc
. Open a web browser and navigate to the console of your target realm : +
$ echo -en "\nhttps://$rhsso_hostname/auth/admin/$rhsso_realm/console\n\n"
. Click the Register
link:
+
image::images/register_link.png[]
. Fill in all fields of the registration form. Ensure you are using a valid email.
. Click Register
.
. Expect your browser to be redirected to a page indicating the need to verify your email and account:
+
image::images/email_verification.png[]
. Check your email for a verification request similar to the following:
+
image::images/registration_email.png[]
. In the email, click the link to email address verification
.
. Your browser should now be re-directed to the homepage of your target SSO realm
+
image::images/realm_homepage.png[]
+
NOTE: This newly registered realm user has administrative access to all settings of a realm.
. In a terminal window of your local machine, set environment variables specific to this new realm user: +
$ realmUserId=
. In your browser, navigate to the Clients
of your realm.
+
Notice the presence of 5 default SSO clients. Each of them is configured to allow for different OAuth2 and OIDC protocols.
+
image::images/default_sso_clients.png[]
=== Generic Realms: Explore Clients
A client, in the context of OAuth2 terminology, is an application requesting access to a protected resource on behalf of a Resource Owner. The Resource Owner is typically an end user (also known as an entity) and the protected resource might be confidential attributes from one or more identities of that Resource Owner/entity.
RH-SSO allows for the creation of custom clients. For the purpose of this lab however, you will use one of the default clients that comes out-of-the-box with your realm.
==== Review realm-management
This is a bearer-only client that will be affiliated with a secured backend service in a later section of this lab.
A bearer-only client is used for back-end services that do not initiate a login with Red Hat SSO, but require a valid ID token.
The ID token is typically used by the back-end service to determine access to resources via role-based access control (RBAC).
==== Review admin-cli
In this lab, you also define a second client called admin-cli
. admin-cli
is an OAuth2 client. You use admin-cli
later in this lab for testing purposes only.
Note that admin-cli
is enabled to use the OAuth2 flow called Resource Owner Password Credentials
.
Via this flow, the client allows exchanging user ID and password credentials--in this case, your SSO user--for an access token. This token can then be used to access the REST APIs of your business services.
== Generic Realms: Endpoints
=== Overview
Your Red Hat SSO server implements an endpoint called well-known
that lists other endpoints and configuration options relevant to the OAuth2/OpenID Connect implementation of Red Hat Single Sign-On.
The purpose of this section is to expose a user (ie: a student) to some of the well-known
endpoints of the OIDC specification.
These well-known
endpoints are specific to the user's SSO realm.
Red Hat SSO also exposes realm administration endpoints. A few of these realm admin endpoints are also introduced in this section.
This entire section can be copy-n-pasted into the lab instructions of your course.
The following utilities will need to be installed on your local machine:
. openssl
. keytool
: (from the Java Development Kit)
=== OIDC well-known Endpoints
. Ensure that the link:https://stedolan.github.io/jq/[jq] utility is installed on your machine.
. Execute the following and study the response: +
$ curl -X GET "https://$rhsso_hostname/auth/realms/$rhsso_realm/.well-known/openid-configuration" | python -m json.tool
- The various OpenID Connect
Relying Party
clients invoke these Red Hat SSO endpoints throughout the remainder of this lab. - Two endpoints are of particular interest: ** link:http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint[Authorization Endpoint] ** link:http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint[Token Endpoint]
. View the list of grant_types_supported
types:
+
$ curl -X GET "https://$rhsso_hostname/auth/realms/$rhsso_realm/.well-known/openid-configuration" | jq -r '.grant_types_supported'
- .Sample Output
[ "authorization_code", "implicit", "refresh_token", "password", "client_credentials" ]
- This list of
grant_type_supported
types corresponds to the various OAuth2/OpenID Connect flows that are supported by the Red Hat SSO server.
=== OAuth2 Access Token
. Ensure the values of the $rhsso_realm, $realmUserId and $realmPasswd environment variables are set in your shell.
. Retrieve OIDC Access Token from a realm: +
$ retrieve_token_url="https://$rhsso_hostname/auth/realms/$rhsso_realm/protocol/openid-connect/token"
$ TKN=$(curl -X POST "$retrieve_token_url"
-H "Content-Type: application/x-www-form-urlencoded"
-d "username=$SSO_SITE_ADMIN_USERNAME"
-d "password=$SSO_SITE_ADMIN_PASSWORD"
-d "grant_type=password"
-d "client_id=admin-cli"
| sed 's/.access_token":"//g' | sed 's/".//g')
- NOTE: This response from OAuth2 token endpoint could additionally be parsed for the refresh_token. The refresh_token could then optionally be used to refresh the access token when the access token expires.
=== Realm Public Key In this section you will retrieve the public key of your realm. Later, this public key can be included in keycloak adapter that is injected into a backend business service. The backend service will then be able to participate in an OpenID Connect flow.
. When a realm is created a key pair and a self-signed certificate are automatically generated. + Keycloak currently only supports RSA signatures so there is only one active keypair. . Set the value of $retrieve_key_url environemnt variable: +
$ retrieve_key_url="https://$rhsso_hostname/auth/admin/realms/$rhsso_realm/keys"
. Retrieve value of RSA key: +
$ RSA_PUB_KEY=$(curl -k -X GET "$retrieve_key_url"
-H "Authorization: Bearer $TKN"
| jq -r '.keys[] | select(.type=="RSA") | .publicKey'
)
$ echo $RSA_PUB_KEY MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAktETcy8xI/rxit6DH9LW1kAB/0XKiV5eujQL9rL+9WD9ZruC2hMFKhme+kyNjNVCf2cqiY0IChmSqTrfm7OcL3k+Rfq91zDgMI19pszoMGyG2Ek2UZYWNBhydBIPBr70njec7Vq8a/bn88evEROetlUHWWcSuwqsiooHD3RNzuDgRB+2ztim8KttusbZxGPGjIjCNlFEBVetfxpHbTBCeN8dAdOiYRjmW6QVxEUH9m0Hcvcw8XwEyVRvd1HpkzMK0BVBnN73d/G743pvyc2huv/Cj+zoBipxNwKJ8rnPGupBVK/17WmyFgi+CDhZww8EwRiSSwrow+Qv1hUP9bnoFwIDAQAB
=== Optional: View Realm RSA certificate
. Retrieve value of Realm RSA certificate: +
$ RSA_CERT=$(curl -k -X GET "$retrieve_key_url"
-H "Authorization: Bearer $TKN"
| jq -r '.keys[] | select(.type=="RSA") | .certificate'
)
$ echo $RSA_CERT MIICuTCCAaECBgFcJ85oAjANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVyaHRfZ3B0ZV8zc2NhbGVfcmVhbG0wHhcNMTcwNTIwMjEzOTE3WhcNMjcwNTIwMjE0MDU3WjAgMR4wHAYDVQQDDBVyaHRfZ3B0ZV8zc2NhbGVfcmVhbG0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCS0RNzLzEj+vGK3oMf0tbWQAH/RcqJXl66NAv2sv71YP1mu4LaEwUqGZ76TI2M1UJ/ZyqJjQgKGZKoGt+bs5wveT5F+r3XMOAwjX2mzOgwbIbYSTZRlhY0GHJ0Eg8GvvSeN5ztWrxr9ufzx68RE562VQdZZxK7CqyKigcPdE3O4OBEH7bO2Kbwq226xtnEY8aMiMI2UUQFV61/GkdtMEJ43x0B06JhGOZbpBXERQf2bQdy9zDxfATJVG93UemTMwrQFUGc3vd38bvjem/JzaG6/8KP7OgGKnE3Aonyuc8a6kFUr/XtabIWCL4IOFnDDwTBGJJLCujD5C/WFQ/1uegXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABM81ImbVgN5VoD7b7RCH0jYW9pUzIwjUUWU3P1E+RGMwyyhgCwMK/wDpzty5jno7XyE1oePgZg8OmXY2FGlWi0JzCF8WGsHVEZWiKPkSs9miz5+x5VqKUEM4nrmF3OgEFPH/gJPhx8/GNutSpf0AIHcVDeWL7nayyjMZWSdXm82crd5gZXDSmNgjjeqhTCPFCrMv3nQq9wsL3jDEc7pOAkblsu83GCyD0WmdHqAY/EdT/Jz1b2lJw/Oda6b9Hg93MvnnaJMam7Q7q4K0oRFaYD0ZtYm926YQg5f1iLzoocuouuOeHrMfZQOqb96iaGYeQF+GghpfNXwKMLOhQh3tJM=
. Create a PEM file from certificate: .. Set an environment variable the defines path to target x.509 certificate in PEM format: +
$ realm_cert=/tmp/$rhsso_realm.pem
.. Create x.509 certificate in PEM format: +
$ echo "-----BEGIN CERTIFICATE-----" > $realm_cert; echo $RSA_CERT >> $realm_cert; echo "-----END CERTIFICATE-----" >> $realm_cert
. View details of SSO Realm certificate: +
$ openssl x509 -in $realm_cert -text -noout
== Generic Realms: Smoke Test OAuth2 Auth and RBAC
=== Overview of secured business service
Very often, services need identity information about the end user invoking its functionality. One common use case is the need for role-based access control (RBAC). Based on the end user's role, the business service determines whether to grant access to the resource being invoked.
NOTE: This RBAC handled by the back-end business service is different than policy and rate limit enforcement that is handled by an API Manager.
In this section of the lab, you will smoke test OAuth2/OIDC security using a simple backend service written using link:https://thorntail.io/[Thorntail Java Microprofile] technology.
The source code for this simple service is found link:https://github.com/gpe-mw-training/3scale_onpremise_implementation_labs/tree/secure/services/wfswarm-date-service[here].
In order for your microprofile back-end service to implement RBAC, the following needs to happen:
- Weave RBAC rules into your business service: For the purposes of this lab, this step has already been done for you.
In particular, note the following new code snippet in the
src/main/resources/project-defaults.yml
configuration file:
swarm: deployment: wf-swarm-oauth.war: web: login-config: auth-method: KEYCLOAK security-constraints: - url-pattern: / methods: [GET] roles: [admin]
Add a Keycloak adapter: Your back-end service needs to be injected with functionality that allows it to communicate with the Red Hat SSO server. In particular, it needs to pull the Red Hat SSO server realm's public key.
Unpack JWT using the Red Hat SSO realm's public key: At runtime, the back-end business service is passed a JSON Web Token (JWT) with identity information about the end user. This identity information includes the end user's
role
. Once the JWT is received, the embedded Keycloak adapter validates the authenticity of the JWT (using the Red Hat SSO realm's public key). Once the JWT is verified as having originated from your Red Hat SSO server, the business service uses the identity information to decide whether the end user has correct credentials to access the desired resource.
=== Deployment In this section, you will define a DeploymentConfig for a simple backend business service that is to be secured via the OIDC protocol.
. Create a new project for your mock RESTful business service applications: +
$ oc new-project $OCP_PROJECT_PREFIX-bservices
--display-name="$OCP_PROJECT_PREFIX-bservices"
--description="Business Services to be secured using OIDC"
. If not already there, switch to this new project: +
$ oc project $OCP_PROJECT_PREFIX-bservices
. Create a new application based on a simple RESTful service implemented using Wildfly Swarm: +
$ oc new-app
--template=wf-swarm-oauth
--param=JAVA_OPTIONS="-Djavax.net.debug=ssl -Dswarm.keycloak.json.path=/app/rhsso-config/keycloak.json"
- This command creates a deployment config with a paused pod.
The pod includes a Java based container.
The
--XmX
of the JVM in the container is set to 80% of 1Gi; so about 800 MB
=== keycloak.json
adapter
. Define keycloak adapter specific to the existing bearer-only
client called: realm-management
:
+
$ echo " { "realm": "$rhsso_realm", "bearer-only": "true", "auth-server-url": "http://$rhsso_hostname/auth", "ssl-required": "external", "realm-public-key": "$RSA_PUB_KEY", "resource": "realm-management", "use-resource-role-mappings": "true" }" > /tmp/keycloak.json
. Create a ConfigMap from the keycloak.json
file.
+
You then mount it as a volume and point WildFly Swarm to the mounted keycloak.json
.
.. Create a ConfigMap called date-service-rhsso
in the bservices
project on OpenShift from the keycloak.json
file:
+
$ oc create configmap keycloak-resource-cm --from-file=/tmp/keycloak.json
.. Mount the configmap as a volume in the Swarm DC: +
$ oc set volume dc/wf-swarm-oauth --add --overwrite
--name=keycloak-resource-volume
-m /app/rhsso-config
--type=configmap
--configmap-name=keycloak-resource-cm
. Resume the paused deploymentconfig: +
$ oc rollout resume dc/wf-swarm-oauth
. Review the log file of the secure wildfly swarm service. Log statements similar to the following should appear: +
DEBUG [org.keycloak.adapters.undertow.KeycloakServletExtension] (ServerService Thread Pool -- 11) KeycloakServletException initialization DEBUG [org.keycloak.adapters.undertow.KeycloakServletExtension] (ServerService Thread Pool -- 11) using /WEB-INF/keycloak.json DEBUG [org.keycloak.adapters.authentication.ClientCredentialsProviderUtils] (ServerService Thread Pool -- 11) Using provider 'secret' for authentication of client 'realm-management' DEBUG [org.keycloak.adapters.authentication.ClientCredentialsProviderUtils] (ServerService Thread Pool -- 11) Loaded clientCredentialsProvider secret DEBUG [org.keycloak.adapters.authentication.ClientCredentialsProviderUtils] (ServerService Thread Pool -- 11) Loaded clientCredentialsProvider jwt DEBUG [org.keycloak.adapters.KeycloakDeployment] (ServerService Thread Pool -- 11) resolveUrls DEBUG [org.keycloak.adapters.KeycloakDeploymentBuilder] (ServerService Thread Pool -- 11) Use authServerUrl: https://sso-rhsso-sso0.apps.3295.openshift.opentlc.com/auth, tokenUrl: https://sso-rhsso-sso0.apps.3295.openshift.opentlc.com/auth/realms/realm1/protocol/openid-connect/token, relativeUrls: NEVER DEBUG [org.keycloak.adapters.undertow.KeycloakServletExtension] (ServerService Thread Pool -- 11) Keycloak is using a per-deployment configuration. DEBUG [org.keycloak.adapters.wildfly.WildflyKeycloakServletExtension] (ServerService Thread Pool -- 11) creating WildflyAuthenticationMechanism DEBUG [org.keycloak.adapters.undertow.KeycloakServletExtension] (ServerService Thread Pool -- 11) Setting jsession cookie path to: /
... INFO [org.wildfly.swarm] (main) WFSWARM99999: WildFly Swarm is Ready
=== Test OIDC secured business service:
. Execute the following to acquire a new access token (since the original access token is likely expired by now): +
$ TKN=$(curl -k -X POST "$retrieve_token_url"
-H "Content-Type: application/x-www-form-urlencoded"
-d "username=$realmUserId"
-d "password=$realmPasswd"
-d "grant_type=password"
-d "client_id=admin-cli"
| sed 's/.access_token":"//g' | sed 's/".//g')
- Technically, a best approach would be to use the refresh_token facility of OAuth2 rather than re-generate a new access token.
With this approach, the parameters
grant_type=refresh_token&refresh_token=$REFRESH_TOKEN
is specified in the body of the request. The value of $REFRESH_TOKEN could have been acquired by parsing it from the response of the original request for OAuth2 tokens.
. Execute the following to invoke the backend RESTful service using the OAUTH2 access token: +
$ curl -k -v -X GET
-H "Authorization: Bearer $TKN"
https://oc get route/wf-swarm-oauth --template "{{.spec.host}}"
/time/now
. The response back should include headers and a body similar to the following: +
...
- TLSv1.2 (IN), TLS handshake, Finished (20):
- SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
- ALPN, server did not agree to a protocol
- Server certificate:
- subject: CN=master.3295.openshift.opentlc.com
- start date: Nov 13 20:48:38 2018 GMT
- expire date: Feb 11 20:48:38 2019 GMT
- issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
- SSL certificate verify ok.
GET /time/now HTTP/1.1 Host: wf-swarm-oauth-jb-bservices.apps.3295.openshift.opentlc.com User-Agent: curl/7.61.1 Accept: / Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJIbm5ZV1NMM1pqSTh0T0VHZVlWZHpIbXVVSFpYSDdJbHM1dEJkSVU0VHMwIn0.eyJqdGkiOiIwOWQyZDZhMC1kZmRjLTRjNTctOWZjOC1mMDVjZGU0MjFjMjAiLCJleHAiOjE1NDIyMTQwMzYsIm5iZiI6MCwiaWF0IjoxNTQyMjEzNzM2LCJpc3MiOiJodHRwczovL3Nzby1yaHNzby1zc28wLmFwcHMuMzI5NS5vcGVuc2hpZnQub3BlbnRsYy5jb20vYXV0aC9yZWFsbXMvcmVhbG0xIiwiYXVkIjoiYWRtaW4tY2xpIiwic3ViIjoiNDc2Y2ZlOTAtMTQwMC00ODUwLTg5M2YtNjNlNWM5MjFmMjYxIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNmQ2ODcyZjgtMjQwZC00MWVjLWE4NmQtM2FiOGEyZTVmZWE5IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6W10sInJlc291cmNlX2FjY2VzcyI6e30sIm5hbWUiOiJKZWZmIEJyaWRlIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiamJyaWRlIiwiZ2l2ZW5fbmFtZSI6IkplZmYiLCJmYW1pbHlfbmFtZSI6IkJyaWRlIiwiZW1haWwiOiJqYnJpZGUrMUByZWRoYXQuY29tIn0.PoqUaPncOt9GFpCdHTQE1zuVK3FHTrgkBhmthww9geM-pbV84UTLXr1ggD-v7s9DGYAaVTe7ZUcda0DT5ioADpPuik6qADPf0ZkkWGQiQ6aieBxuTikM0NeeM58Vwhcr5lDphD0VY70SK_aaevUKkZFkmKxUxeUj-8TZob0n5Jip376D8NvDtfoZEhiWngBW58H1J4-sg27qzaUgVqaEoM7zUEvZocgD6j6rUqnThNREJVMi3sUYli6y_LzzqrMwZrWTlxAIYcR6OQ7GK-WTtN_F18_5O28Zaojq1QRHjhotcHPXCTrJaf4XqNEZFGML8yVh5lgzd-HVMt3Lrd_15Q
< HTTP/1.1 200 OK
...
{"value" : "The time is 2018-11-14T16:44:57.576Z"}
. Review the log file of the secure wildfly swarm service. Log statements similar to the following should appear: +
DEBUG [org.keycloak.adapters.PreAuthActionsHandler] (default task-1) adminRequest http://wf-swarm-oauth-jb-bservices.apps.3295.openshift.opentlc.com/time/now DEBUG [org.keycloak.adapters.wildfly.WildflyRequestAuthenticator] (default task-1) propagate security context to wildfly DEBUG [org.keycloak.adapters.RequestAuthenticator] (default task-1) User '476cfe90-1400-4850-893f-63e5c921f261' invoking 'http://wf-swarm-oauth-jb-bservices.apps.3295.openshift.opentlc.com/time/now' on client 'realm-management' DEBUG [org.keycloak.adapters.RequestAuthenticator] (default task-1) Bearer AUTHENTICATED DEBUG [org.keycloak.adapters.AuthenticatedActionsHandler] (default task-1) AuthenticatedActionsValve.invoke http://wf-swarm-oauth-jb-bservices.apps.3295.openshift.opentlc.com/time/now DEBUG [org.keycloak.adapters.AuthenticatedActionsHandler] (default task-1) Policy enforcement is disabled. INFO [stdout] (default task-1) userId = 476cfe90-1400-4850-893f-63e5c921f261
== Appendix
=== Set RH-SSO Log to DEBUG
(Optional)
At times, you may want to increase the log level of the org.keycloak
java package of your RH-SSO server from INFO
to DEBUG
.
Doing so is beneficial when attempting to troubleshoot problems with security protocols and RH-SSO.
. Open a remote shell session to your RH-SSO pod: +
$ oc rsh -n $rhsso_project
$(oc get pod -n $rhsso_project | egrep "sso-[1-9]" | awk '{print $1}')
. Edit the master configuration file for your RH-SSO server: .. Open the configuration file in an editor: +
$ vi /opt/eap/standalone/configuration/standalone-openshift.xml
.. Search for the org.jboss.as.config
field:
+
<logger category="org.jboss.as.config">
<level name="DEBUG"/>
</logger>
.. Just below this XML block, add a similar block as follows: +
<logger category="org.keycloak">
<level name="DEBUG"/>
</logger>
.. Save the change and exit.
. Restart the keycloak JVM within the same RH-SSO pod: .. Change to the following directory: +
cd /opt/eap/bin/
.. Execute this command: +
./jboss-cli.sh --connect ':reload'
Red Hat Single Sign-On Multi-Realm Automated Provisioning
ansible-galaxy install gpe-mw-ansible-org/rh-sso-multi-realm