OpenShift Container Platform

In this article, we'll cover microservice security concepts by using protocols such as OpenID Connect with the support of Red Hat Single Sign-On and 3scale. Working with a microservice-based architecture, user identity, and access control in a distributed, in-depth form must be carefully designed. Here, the integration of these tools will be detailed, step-by-step, in a realistic view.

This article exemplifies the use of tools that can securely run your businesses, avoiding using homemade solutions, and protecting your services by using an API gateway, preventing your applications from being exposed to the public network. The use of an API gateway also provides additional access control, monetization, and analytics.

security

Technology Version
Spring Boot 2.1.8.RELEASE
Apache Camel 7.4.0.fuse-740036-redhat-00002
(w/ spring boot 1.5.22.RELEASE)
3scale 2.6
Red Hat Single Sign-On (RHSSO) 7.3.3
(based on keycloak 4.8)

TL;DR: This is a demonstration of how to protect APIs with Red Hat Single Sign-On (Keycloak) and 3scale.

How to protect APIs with Red Hat Single Sign-On (Keycloak) and 3scale graphics

This is a lengthy article with step-by-step instructions, screenshots of products, and architecture concepts. All source code is hosted on GitHub.

Note: This is a proof of concept. In production environments, additional configurations will be needed regarding scalability, security (refinement) and using a proper CA-trusted certificate.

The use case scenario

The main purpose of this tutorial is to achieve concepts regarding the security of microservices using a whole use case scenario. A web app is offered to promote a more natural understanding of all API calls and authorizations used.

The All APIs catalog is exposed below.

auth-integration-api endpoints

:8081

Method URI Description
GET /health API actuator embedded health.
GET /metrics API actuator embedded metrics.

 

 

 

:8080

Method URI Description Secured?
POST /api/v1/product Create a new product. true
DELETE /api/v1/product/* Delete product by ID. true
PUT /api/v1/product/* Update product by ID. true
GET /api/v1/product$ Retrieve all products true
GET /api/v1/product/* Same as above. true
GET /api/v1/status Check Integration API health. true
GET /api/v1/product/status Check Product API health. true
GET /api/v1/supplier/status Check Supplier API health. true
GET /api/v1/stock/status Check Stock API health. true
GET /api/v1/stock/maintenance Call Stock API maintenance. true
GET /api/v1/supplier/maintenance Call Supplier API maintenance. true

stock-api endpoints

Method URI Description
GET /api/v1/sync Stock Maintenance
GET /actuator/health Supplier Maintenance

supplier-api endpoints

Method URI Description Secured?
GET /api/v1/sync Supplier Maintenance true
GET /actuator/health Supplier Maintenance true

product-api endpoints

Method URI Description Secured?
GET /api/v1/product Retrieve all products. true
GET /api/v1/product/{id} Retrieve product by ID. true
POST /api/v1/product Create a new product. true
PUT /api/v1/product/{id} Update product by ID. true
DELETE /api/v1/product/{id} Delete product by ID. true

Security lab

Each endpoint has its own specificity, so to drive our test scenarios, I've ended up with three simple questions:

  • Will this API be protected by an Integration Layer (FUSE)?
  • Will this API be exposed as a unique service on 3scale AMP? (This factor enables API self-service subscriptions for external clients.)
  • Will this API be managed by RHSSO (Keycloak) and have its client-id, groups, and roles?

So, I came up with the following requirements matrix:

requirements matrix

As we can see, each API has differences, and we will strive to demonstrate each one in this microservices security lab.

Step 1: Project creation

Create this project as follows:

export PROJECT_NAMESPACE=microservices
# login into openshift platform
oc login https://master.<>.com:443 --token=<>
# create a new project
oc new-project microservices-security --description="microservices security" --display-name="microservices-security"

Step 2: Nexus prototype deploy

Provide a Sonatype Nexus instance in the microservices namespace. Detailed instructions can be found in this readme.

Step 3: 3scale AMP deploy

You must also provision a 3scale AMP into your Red Hat Openshift Container Platform. Refer to the documentation on how to install the 3scale application.

Step 4: Red Hat Single Sign-On deploy

Additionally, install Red Hat Single Sign-On for this example. Refer to the documentation on how to install the RHSSO application.

Step 5: Nexus environment setup

Set up your Nexus environment as follows:

export PROJECT_NAMESPACE=microservices-security
git clone https://github.com/aelkz/microservices-security.git

cd microservices-security/
# download maven settings.xml file
curl -o maven-settings-template.xml -s https://raw.githubusercontent.com/aelkz/microservices-security/master/_configuration/nexus/maven-settings-template.xml
# change mirror url using your nexus openshift route
export NEXUS_NAMESPACE=cicd-devtools
export MAVEN_URL=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-group/
export MAVEN_URL_RELEASES=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-releases/
export MAVEN_URL_SNAPSHOTS=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-snapshots/
awk -v path="$MAVEN_URL" '/<url>/{sub(/>.*</,">"path"<")}1' maven-settings-template.xml > maven-settings.xml
rm -fr maven-settings-template.xml

Step 6: Create Red Hat Container Catalog secret

In order to import Red Hat container images, you must create a secret and set up your credentials on OpenShift:

NOTE. In order to import Red Hat container images, you must setup your credentials on openshift. See: https://access.redhat.com/articles/3399531
# The config.json can be found at: /var/lib/origin/.docker/ on openshift master node.
# create a secret with your container credentials

export $PROJECT_NAMESPACE=microservices-security

oc delete secret redhat.io -n $PROJECT_NAMESPACE
oc create secret generic "redhat.io" --from-file=.dockerconfigjson=config.json --type=kubernetes.io/dockerconfigjson -n $PROJECT_NAMESPACE
oc create secret generic redhat.io --from-file=.dockerconfigjson=config.json --type=kubernetes.io/dockerconfigjson -n $PROJECT_NAMESPACE
oc secrets link default redhat.io --for=pull -n $PROJECT_NAMESPACE
oc secrets link builder redhat.io -n $PROJECT_NAMESPACE

Step 7: RHSSO realms configuration

In this step, we will configure realms on RHSSO to register all five applications.

  1. Logging into RHSSO.
  2. Create three realms with default settings:
    • 3scale-api
    • 3scale-admin
    • 3scale-devportal

After creating the realms, you'll have something like this:

RHSSO realms configuration

  1. On the 3scale-api realm, create a client 3scale with the following definition:

create a client 3scale figure

Leave these fields blank:

    • root URL
    • base URL
    • admin URL
  1. On the Service Account Roles tab, assign the role manage-clients from realm-management.
  2. Copy and save the client-secret that was generated for this client. This secret will be used later to configure OAuth service authentication on 3scale, and will look something like this: 823b6ek5-1936-42e6-1135-d48rt3a1f632.
  3. Under the realm 3scale-api, create a new user with the following definition:

create a new user image

  1. Set a new password for this user on the Credentials tab with temporary=false.
  2. Set the Email Verified attribute totrue on the Details tab.

Step 8: 3scale microservices configuration

In this step, we register the APIs and configure them to enable 3scale automatic synchronization with RHSSO. Let's set up the auth-integration-api and the supplier-api:

  1. Create a new API on the 3scale admin portal. You can hit the NEW API button on the main dashboard:

Create a new API on the 3scale admin portal image

This new API will represent the auth-integration-api, which we previously deployed:

new API screen

  1. Navigate through the Configuration menu under Integration to set up the API mappings and security:

Set up the API mappings and security screen

  1. Choose APIcast for the gateway:

Choose APIcast for the gateway

  1. Choose OpenID Connect in Integration settings:

Choose OpenID Connect in Integration settings

Note: OpenID Connect is chosen because we will to protect our APIs with the OAuth2 capabilities provided by RHSSO.

  1. Click:

Add base url of API and Save configuration

  1. Define the Private Base URL (your auth-integration-api URL), the Staging Public Base URL, and the Production Public Base URL:

Enter Correct domain under each URL

Note: Set the correct domain under each URL, which will become your API route on OpenShift.

  1. Define all of the mapping rules for this API, according to the following table:
Verb Pattern + Metric or Method
POST /api/v1/product 1 hits
DELETE /api/v1/product/* 1 hits
PUT /api/v1/product/* 1 hits
GET /api/v1/product$ 1 hits
GET /api/v1/product/* 1 hits
GET /api/v1/status 1 hits
GET /api/v1/product/status 1 hits
GET /api/v1/supplier/status 1 hits
GET /api/v1/stock/status 1 hits
GET /api/v1/stock/maintenance 1 hits
GET /api/v1/supplier/maintenance 1 hits
  1. Define the authentication mechanism for this API:

Define the authentication mechanism for this API

  1. Configure the API policies required to enable proper communication between resources inside the OpenShift Container Platform:
Configure the API policies
Please follow the next steps carefully:
  1. Select Authorization Code Flow, Service Accounts Flow, and Direct Access Grant Flow under the OIDC AUTHORIZATION FLOW section.
  2. In Credentials location set As HTTP Headers.
  3. In the Policies section, add (in this order) CORS and 3scale APIcast.
  4. Expand CORS configuration, and set the following:
    • ALLOW_HEADERS adds a 1x1 per input array:
Enabled=checked
ALLOW_HEADERS
3scale CORS Policy: HEADERS
Content-Type
Authorization
Content-Length
X-Requested-With
Origin
Accept
X-Requested-With
Content-Type
Access-Control-Request-Method
Access-Control-Request-Headers
Accept-Encoding
Accept-Language
Connection
Host
Referer
User-Agent
Access-Control-Allow-Origin
X-Business-FooBar

Note: The last header is used only for testing purposes.

    • ALLOW_METHODS adds a 1x1 per input array:
allow_credentials=checked
ALLOW_METHODS
3scale CORS Policy: HTTP Methods
GET
HEAD
POST
PUT
DELETE
OPTIONS
  1. Leave allow_origin empty, and the rest as default.
  2. Save the CORS configuration.

Note: After every change, remember to promote the staging configuration to production, by clicking:
Promote the staging configuration to production

Your auth-integration-api is ready to be used.

Repeat the same sequence of steps in this section for the Supplier API. This API has only two mapping rules:

Verb Pattern + Metric or Method
POST /api/v1/sync 1 hits
GET /actuator/health 1 hits

Step 9: 3scale microservices application plans

Let's define the API's application plans. These plans will be used upon client registration for creating a new application:

3scale microservices application plans

Click Click on Create Application Plan under the Applications/Application Plans menu, then set the following configuration:

Create Application Plan

After doing this, click the Publish link to publish the application plan.

Follow the same steps for the Supplier API. Remember to publish this application plan as well.

After you have done all of the previous steps, you'll get something like this:

Publish the Application plan

Step 10: 3scale microservices application

Navigate through the Audience menu and under Accounts/Listing, click Create a new account to create a new account. Then, create a new account with your credentials for this demo:

Create New Account screen

This action creates a new 3scale application. If the application couldn't be created, just hit the Create Application Link link.

This new application is created for use with the auth-integration-api. A client-id and a Client-Secret will be generated automatically and pushed into RHSSO in the 3Scale-api realm by the zynnc-que 3scale application:

A client-id screen

After creating the API definition on 3scale, check if the generated client was pushed into the 3scale-api realm on RHSSO. If you're using a self-signed certificate, you'll need to make additional configurations in order to enable zynnc-que 3scale application synchronization. Please refer to the Documentation: Troubleshooting SSL issues and Configure Zync to use custom CA certificates.

To fix this problem, proceed with the self-signed certificate installation:

export THREESCALE_NAMESPACE=3scale26
export THREESCALE_ZYNC_QUE_POD=$(oc get pods --selector deploymentconfig=zync-que -n 3scale26 | { read line1 ; read line2 ; echo "$line2" ; } | awk '{print $1;}')
export RHSSO_URI=sso73.apps.<YOUR-DOMAIN>.com
echo | openssl s_client -showcerts -servername ${RHSSO_URI} -connect ${RHSSO_URI}:443 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > self-signed-cert.pem

# Validate the connection first! must return HTTP/1.1 200 OK
curl -v https://${RHSSO_URI}/auth/realms/master --cacert self-signed-cert.pem
oc exec ${THREESCALE_ZYNC_QUE_POD} cat /etc/pki/tls/cert.pem > zync-que.pem -n ${THREESCALE_NAMESPACE}
cp zync-que.pem zync-que-original.pem
echo '\n# Red Hat Single Sign-On CA '${RHSSO_URI} >> zync-que.pem
cat self-signed-cert.pem >> zync-que.pem

# oc delete configmap zync-que-ca-bundle
oc create configmap zync-que-ca-bundle --from-file=./zync-que.pem -n ${THREESCALE_NAMESPACE}
oc label configmap zync-que-ca-bundle app=3scale-api-management -n ${THREESCALE_NAMESPACE}
oc set volume dc/zync-que --overwrite --add --name=zync-que-ca-bundle --mount-path /etc/pki/tls/zync-que/zync-que.pem --sub-path zync-que.pem --source='{"configMap":{"name":"zync-que-ca-bundle","items":[{"key":"zync-que.pem","path":"zync-que.pem"}]}}' -n ${THREESCALE_NAMESPACE}

oc patch dc/zync-que --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/0/subPath", "value":"zync-que.pem"}]' -n ${THREESCALE_NAMESPACE}
oc exec ${THREESCALE_ZYNC_QUE_POD} cat /etc/pki/tls/zync-que/zync-que.pem -n ${THREESCALE_NAMESPACE}
oc set env dc/zync-que SSL_CERT_FILE=/etc/pki/tls/zync-que/zync-que.pem -n ${THREESCALE_NAMESPACE}

# wait for the container restart and check the logs for any issue.
oc logs -f po/${THREESCALE_ZYNC_QUE_POD}

# Voila! You have the 3Scale in sync with RHSSO using a self-signed certificate.

Step 11: Node.js web application deployment

In this step, we will test all scenarios with a suited NodeJS webapp based on Angular and Bootstrap. This application was designed to ease the understanding process and can be used to give clarification regarding the authorization behavior using our Jon Doe user account:

# Deploy nodejs-web application
# https://access.redhat.com/containers/?tab=images#/registry.access.redhat.com/rhscl/nodejs-10-rhel7

oc import-image rhscl/nodejs-10-rhel7 --from=registry.redhat.io/rhscl/nodejs-10-rhel7 -n openshift --confirm

export APIS_NAMESPACE=microservices-security
export THREESCALE_NAMESPACE=3scale26
export RHSSO_NAMESPACE=sso73
export RHSSO_URL=https://$(oc get route -n ${RHSSO_NAMESPACE} | grep secured | awk '{print $2;}')/auth
export THREESCALE_APP_DOMAIN=<YOUR-DOMAIN>.com
export THREESCALE_API_URL=https://$(oc get routes -n ${THREESCALE_NAMESPACE} | grep auth-integration | grep production | awk '{print $2;}')
export INTEGRATION_HEALTH_URL=http://$(oc get routes -n ${APIS_NAMESPACE} | grep auth-integration | grep metrics | awk '{print $2;}')

echo -e \
" AUTH_CLIENT_ID=<AUTH_INTEGRATION_CLIENT_ID>\n" \
"AUTH_URL=${RHSSO_URL}\n" \
"AUTH_REALM=3scale-api\n" \
"KEYCLOAK=true\n" \
"INTEGRATION_URI=${THREESCALE_API_URL}\n" \
"PRODUCT_PATH=/product\n" \
"STOCK_PATH=/stock\n" \
"SUPPLIER_PATH=/supplier\n" \
"AUTH_CLIENT_SECRET=1d1beebcF1cJd002d51be7a346ab987p\n" \
"NODE_TLS_REJECT_UNAUTHORIZED=0\n" \
> temp

sed "s/^.//g" temp >> nodejs-config.properties

rm -fr temp

# oc delete configmap nodejs-web-config
oc create configmap nodejs-web-config \
--from-literal=AUTH_CLIENT_ID= \
--from-literal=AUTH_URL= \
--from-literal=AUTH_REALM= \
--from-literal=KEYCLOAK= \
--from-literal=INTEGRATION_URI= \
--from-literal=PRODUCT_PATH= \
--from-literal=SUPPLIER_PATH= \
--from-literal=STOCK_PATH= \
--from-literal=AUTH_CLIENT_SECRET= \
--from-literal=NODE_TLS_REJECT_UNAUTHORIZED=

# oc delete all -lapp=nodejs-web
oc new-app nodejs-10-rhel7:latest~https://github.com/aelkz/microservices-security.git --name=nodejs-web --context-dir=/webapp -n ${APIS_NAMESPACE}

# with the properties defined, set the environment variable on nodejs-web container.
oc set env --from=configmap/nodejs-web-config dc/nodejs-web -n ${APIS_NAMESPACE}

Note: Set all environment variables on the nodejs-web container in order to enable API calls properly.

Expose the web app route:

oc create route edge --service=nodejs-web --cert=webapp/server.cert --key=webapp/server.key -n ${APIS_NAMESPACE}

Step 12: Application settings and roles

Now we create our application roles. These roles are assigned to the application users that will be used to log into our web app.

Access the client-id that represents the auth-integration client registered previously by the 3scale application process, then go to the client's Settings tab and apply additional configurations. Valid redirect URIs include:

http://*
https://*

and valid web origins are simply:

*

Go to the Roles tab on the Clients menu on RHSSO (Keycloak) and create the following roles:

Create Roles into Roles tab

Repeat the same steps for the Supplier API client. This client will have only one role defined:

Supplier API Client screen

Note: This client was also generated through 3scale. (You must create two applications: one for auth-integration-api and another for supplier-api.

Step 13: User roles

In this step, we assign all client roles to the john doe user and the service-account user will handle the supplier-service calls inside the auth-integration-api.

Go to the Role Mappings tab on the John Doe user details page in the Users menu. Assign all roles to the user, following the image below:

Go to the Role Mappings tab and Assign all roles to the user

In Step 7 we created the John Doe user. We need to create another user that will be used as a service-account to call the Supplier API inside the auth-integration-api (see line 123 of application.yaml). This user will also have a password, so reset its credentials with 12345. The name of this user can be the id of the Supplier API client-id generated by 3scale appended with the _svcacc suffix (see line 131 of application.yaml).

We also need to assign the SUPPLIER_MAINTAINER role to this user.

NOTE: This procedure is used as an alternative to the token-exchange mechanism, but we could do a more detailed study of other possibilities of consuming third-party APIs by using the token-exchange feature.

At last, create a realm-admin user. This user will serve to consume the RHSSO REST API. Assign the credentials 12345 and all realm-management roles:

Realm admin, role mapping

In the end, we will have three users in the 3scale-api realm:

Three users in the 3scale-api realm screen

Step 14: Archive the SSO-common library jar on Nexus

To make sure the auth-integration-api (Fuse) works correctly, we need to archive a library, and then use that library to enable authentication and authorizations on top of Red Hat Single Sign-On:

# NOTE: To make sure the auth-integration-api (Fuse) works correctly, we need to archive a library that will be used to provide authentication and authorizations capabilities on top of Red Hat Single Sign-On (Keycloak).Then, this library will be used on auth-integration-api to enable such capabilities.

# Deploy auth-sso-common library on nexus
export NEXUS_NAMESPACE=cicd-devtools
export MAVEN_URL=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-group/
export MAVEN_URL_RELEASES=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-releases/
export MAVEN_URL_SNAPSHOTS=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-snapshots/

mvn clean package deploy -DnexusReleaseRepoUrl=$MAVEN_URL_RELEASES -DnexusSnapshotRepoUrl=$MAVEN_URL_SNAPSHOTS -s ./maven-settings.xml -e -X -pl auth-sso-common 

This action creates the following artifact on Nexus:
Archive the SSO-common library jar on Nexus

Step 15: Microservices deployment

Retrieve RHSSO realm public key:

export RHSSO_REALM=3scale-api
export RHSSO_URI=sso73.apps.<YOUR-DOMAIN>.com
export TOKEN_URL=https://${RHSSO_URI}/auth/realms/${RHSSO_REALM}/protocol/openid-connect/token
export THREESCALE_REALM_USERNAME=admin
export THREESCALE_REALM_PASSWORD=12345

TKN=$(curl -k -X POST "$TOKEN_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$THREESCALE_REALM_USERNAME" \
-d "password=$THREESCALE_REALM_PASSWORD" \
-d "grant_type=password" \
-d "client_id=admin-cli" \
| sed 's/.*access_token":"//g' | sed 's/".*//g')

export REALM_KEYS_URL=https://${RHSSO_URI}/auth/admin/realms/${RHSSO_REALM}/keys

RSA_PUB_KEY=$(curl -k -X GET "$REALM_KEYS_URL" \
-H "Authorization: Bearer $TKN" \
| jq -r '.keys[] | select(.type=="RSA") | .publicKey' )

# Create a valid .pem certificate
REALM_CERT=$RHSSO_REALM.pem
echo "-----BEGIN CERTIFICATE-----" > $REALM_CERT; echo $RSA_PUB_KEY >> $REALM_CERT; echo "-----END CERTIFICATE-----" >> $REALM_CERT

# Check the generated .pem certificate
# fold -s -w 64 $REALM_CERT > $RHSSO_REALM.fixed.pem
# openssl x509 -in $RHSSO_REALM.fixed.pem -text -noout
# openssl x509 -in $RHSSO_REALM.fixed.pem -noout -issuer -fingerprint

Then, deploy the parent project:

# Deploy parent project on nexus
mvn clean package deploy -DnexusReleaseRepoUrl=$MAVEN_URL_RELEASES -DnexusSnapshotRepoUrl=$MAVEN_URL_SNAPSHOTS -s ./maven-settings.xml -e -X -N

Now, deploy the stock-api:

# oc delete all -lapp=stock-api
oc new-app openjdk-8-rhel8:latest~https://github.com/aelkz/microservices-security.git --name=stock-api --context-dir=/stock --build-env='MAVEN_MIRROR_URL='${MAVEN_URL} -e MAVEN_MIRROR_URL=${MAVEN_URL}

oc patch svc stock-api -p '{"spec":{"ports":[{"name":"http","port":8080,"protocol":"TCP","targetPort":8080}]}}'

oc label svc stock-api monitor=springboot2-api

Use the provided configmap and secret to set the required variables:

oc create -f configuration/configmap/stock-api-env.yml -n ${PROJECT_NAMESPACE}
oc create -f configuration/secret/stock-api.yml -n ${PROJECT_NAMESPACE}

export APP=stock-api
oc set env dc/${APP} --from=secret/stock-api-secret
oc set env dc/${APP} --from=configmap/stock-api-config

Deploy the supplier-apibut first check all settings in the application.yaml file before continuing. The attributes here must be updated to reflect your actual environment:

  • rest.security.issuer-uri on Line 61.
  • security.oauth2.resource.id on Line 71.
  • security.oauth2.resource.jwt.key-value on Line 75.

# oc delete all -lapp=supplier-api
oc new-app openjdk-8-rhel8:latest~https://github.com/aelkz/microservices-security.git --name=supplier-api --context-dir=/supplier --build-env='MAVEN_MIRROR_URL='${MAVEN_URL} -e MAVEN_MIRROR_URL=${MAVEN_URL}

oc patch svc supplier-api -p '{"spec":{"ports":[{"name":"http","port":8080,"protocol":"TCP","targetPort":8080}]}}'

oc label svc supplier-api monitor=springboot2-api

Use the provided configmap and secret to set the required variables:

oc create -f configuration/configmap/supplier-api-env.yml -n ${PROJECT_NAMESPACE}
oc create -f configuration/secret/supplier-api.yml -n ${PROJECT_NAMESPACE}

export APP=supplier-api

oc set env dc/${APP} --from=secret/supplier-api-secret
oc set env dc/${APP} --from=configmap/supplier-api-config

Deploy the product-api, but again, check all settings in the application.yaml file before continuing. The following attributes must be updated to reflect your actual environment: rest.security.issuer-uri on Line 61, and thesecurity.oauth2.resource.jwt.key-value on Line 75:

# oc delete all -lapp=product-api
oc new-app openjdk-8-rhel8:latest~https://github.com/aelkz/microservices-security.git --name=product-api --context-dir=/product --build-env='MAVEN_MIRROR_URL='${MAVEN_URL} -e MAVEN_MIRROR_URL=${MAVEN_URL}

oc patch svc product-api -p '{"spec":{"ports":[{"name":"http","port":8080,"protocol":"TCP","targetPort":8080}]}}'

oc label svc product-api monitor=springboot2-api

Use the provided configmap and secret to set the required variables:

oc create -f configuration/configmap/product-api-env.yml -n ${PROJECT_NAMESPACE}
oc create -f configuration/secret/product-api.yml -n ${PROJECT_NAMESPACE}

export APP=supplier-api

oc set env dc/${APP} --from=secret/product-api-secret
oc set env dc/${APP} --from=configmap/product-api-config

Step 16: Integration deployment (FUSE)

Now that the microservices APIs are deployed, let’s deploy the integration layer:

# import a new spring-boot camel template
curl -o s2i-microservices-fuse74-spring-boot-camel.yaml -s https://raw.githubusercontent.com/aelkz/microservices-security/master/_configuration/openshift/s2i-microservices-fuse74-spring-boot-camel.yaml

oc delete template s2i-microservices-fuse74-spring-boot-camel -n ${PROJECT_NAMESPACE}
oc create -n ${PROJECT_NAMESPACE} -f s2i-microservices-fuse74-spring-boot-camel.yaml

# NOTE. You may want to check the ..self-signed.yaml template as it uses a customized imagestream for use with self-signed certificates. (see the APPENDIX-README.md for for info)
export NEXUS_NAMESPACE=cicd-devtools
export PROJECT_NAMESPACE=microservices-security
export APP=auth-integration-api
export APP_NAME=auth-integration
export APP_GROUP=com.redhat.microservices
export APP_GIT=https://github.com/aelkz/microservices-security.git
export APP_GIT_BRANCH=master
export MAVEN_URL=http://$(oc get route nexus3 -n ${NEXUS_NAMESPACE} --template='{{ .spec.host }}')/repository/maven-group/
export CUSTOM_TEMPLATE=s2i-microservices-fuse74-spring-boot-camel-selfsigned

# the previous template have some modifications regarding services,route and group definitions.
# oc delete all -lapp=${APP}
oc new-app --template=${CUSTOM_TEMPLATE} --name=${APP} --build-env='MAVEN_MIRROR_URL='${MAVEN_URL} -e MAVEN_MIRROR_URL=${MAVEN_URL} --param GIT_REPO=${APP_GIT} --param APP_NAME=${APP} --param ARTIFACT_DIR=${APP_NAME}/target --param GIT_REF=${APP_GIT_BRANCH} --param MAVEN_ARGS_APPEND='-pl '${APP_NAME}' --also-make'

# Use this param if using a different TAG (example)
# --param BUILDER_VERSION=2.0

# check the created services:
# 1 for default app-context and 1 for /metrics endpoint.
oc get svc -n ${PROJECT_NAMESPACE} | grep ${APP_NAME}

# in order to auth-integration-api call the others APIs, we need to change it's configuration:
curl -o application.yaml -s https://raw.githubusercontent.com/aelkz/microservices-security/master/_configuration/openshift/auth-integration/application.yaml

# NOTE. If you have changed the service or application's name, you need to edit and change the downloaded application.yaml file with your definitions.

# create a configmap and mount a volume for auth-integration-api
oc delete configmap ${APP} -n ${PROJECT_NAMESPACE}

oc create -f configuration/configmap/auth-integration-api-env.yml -n ${PROJECT_NAMESPACE}
oc create -f configuration/secret/auth-integration-api.yml -n ${PROJECT_NAMESPACE}

oc set env dc/${APP} --from=secret/auth-integration-api-secret
oc set env dc/${APP} --from=configmap/auth-integration-api-config

Note: All application roles are prefixed with ROLE_ on source-code. This prefix can be changed if you want on line 80 in the ../configuration/security/JwtAccessTokenCustomizer.java class. On RHSSO, these roles are registered without this prefix. See this stack overflow reference.

Step 17: Testing the NodeJS application with Red Hat Single Sign-On

Open the NodeJS web app in your browser:

export MICROSERVICES_NAMESPACE=microservices-security
echo http://$(oc get route nodejs-web -n ${MICROSERVICES_NAMESPACE} --template='{{ .spec.host }}')

If you're using a self-signed certificate, the browser will request authorization to open an insecure URL. Navigate through the menus and test all actions by clicking on every button to see the final result. If an action returns 401 or 403, there is probably pending configuration on 3scale, or missing or invalid credentials on some application. If you get HTTP error 500, maybe the application is unavailable. Try changing Jon Doe roles and check every situation after refreshing the access token:

Testing the NodeJS application with Red Hat Single Sign-On

(Click on the image to see it larger.)

Testing the NodeJS application with Red Hat Single Sign-On

I hope you enjoyed this tutorial. The troubleshooting was somewhat difficult because of all the OAuth2 adapters and security mechanisms involved. Please let me know if you want to improve something, or add more context to this PoC. Thank you.

References

Last updated: October 31, 2023