OpenShift Access Portal - TremoloSecurity/OpenUnison GitHub Wiki
This quickstart will help you quickly create a self service access portal for your OpenShift cluster. To get started you will need:
- OCP 4.x
- A MySQL or MariaDB database
- Certificate/DNS ready for use
- A SAML2 Identity Provider (IdP) such as Active Directory Federation Services
- Credentials for Red Hat Connect to pull images (https://connect.redhat.com)
- An SMTP server to notify users of approvals and other events
Setup MySQL
Create a database called unison
and grant admin privileges to a user called unison
. The operator and OpenUnison will generate all of your tables for you.
Deploy The OpenUnison Operator
- Create the namespace
openunison
-oc new-project openunison
- From inside OCP4, go to the
Operator Hub
and look for the OpenUnison certified operator:
Make sure you are in the openunison
project so the operator is installed into the correct project
Create the Source Secret
You never want to store secrets in a custom resource, so prior to deploying OpenUnison you'll need to create a Secret
in your project that stores information about the database connection, connection for the OpenShift console, etc. Here's an example of the Secret
you'll need to create:
kind: Secret
apiVersion: v1
metadata:
name: orchestra-secrets-source
namespace: openunison
data:
OU_JDBC_PASSWORD: XXXXXXXXXX
OU_OIDC_OPENSHIFT_SECRET:XXXXXXXXXX
REG_CRED_PASSWORD: XXXXXXXXXX
SMTP_PASSWORD: XXXXXXXXXX
unisonKeystorePassword: XXXXXXXXXX
type: Opaque
Authorize OpenUnison to Provision Access to OpenShift
As a cluster administrator, run the following commands:
oc adm groups new cluster-admins
oc adm policy add-cluster-role-to-group cluster-admin cluster-admins
oc adm policy add-cluster-role-to-user cluster-admin system:serviceaccount:openunison:openunison-orchestra
This will allow OpenUnison to create new projects and users.
Deploy OpenUnison
Use the below OpenUnison custom resource as a starting point:
---
apiVersion: openunison.tremolo.io/v1
kind: OpenUnison
metadata:
name: orchestra
spec:
openshift:
git:
repo: https://github.com/openunison/openunison-openshift-saml2.git
branch: master
dir: "/"
builder_image: registry.connect.redhat.com/tremolosecurity/openunison-s2i-10
replicas: 1
enable_activemq: true
activemq_image: registry.connect.redhat.com/tremolosecurity/activemq:latest
dest_secret: orchestra
source_secret: orchestra-secrets-source
hosts:
- names:
- name: orchestra.apps.mydomdain.com
env_var: OU_HOST
ingress_name: openunison
secret_data:
- unisonKeystorePassword
- REG_CRED_PASSWORD
- OU_JDBC_PASSWORD
- OU_OIDC_OPENSHIFT_SECRET
- SMTP_PASSWORD
non_secret_data:
- name: REG_CRED_USER
value: xyz
- name: OPENSHIFT_CONSOLE_URL
value: https://console-openshift-console.apps.mydomdain.com
- name: OU_HIBERNATE_DIALECT
value: org.hibernate.dialect.MySQL5InnoDBDialect
- name: OU_JDBC_DRIVER
value: com.mysql.jdbc.Driver
- name: OU_JDBC_URL
value: jdbc:mysql://mariadb-101-centos7.openunison.svc.cluster.local/unison
- name: OU_JDBC_USER
value: unison
- name: OU_JDBC_VALIDATION
value: SELECT 1
- name: OU_OIDC_OPENSHIFT_REIDRECT
value: https://oauth-openshift.apps.mydomdain.com/oauth2callback/openunison
- name: OU_QUARTZ_DIALECT
value: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
- name: OU_QUARTZ_MASK
value: "10"
- name: SESSION_INACTIVITY_TIMEOUT_SECONDS
value: "900"
- name: SMTP_FROM
value: [email protected]
- name: SMTP_HOST
value: smtp.mydomdain.com
- name: SMTP_PORT
value: "587"
- name: SMTP_TLS
value: "true"
- name: SMTP_USER
value: [email protected]
openunison_network_configuration:
open_port: 8080
open_external_port: 80
secure_port: 8443
secure_external_port: 443
secure_key_alias: "unison-tls"
force_to_secure: true
activemq_dir: "/tmp/amq"
quartz_dir: "/tmp/quartz"
client_auth: none
allowed_client_names: []
ciphers:
- TLS_RSA_WITH_RC4_128_SHA
- TLS_RSA_WITH_AES_128_CBC_SHA
- TLS_RSA_WITH_AES_256_CBC_SHA
- TLS_RSA_WITH_3DES_EDE_CBC_SHA
- TLS_RSA_WITH_AES_128_CBC_SHA256
- TLS_RSA_WITH_AES_256_CBC_SHA256
path_to_deployment: "/usr/local/openunison/work"
path_to_env_file: "/etc/openunison/ou.env"
saml_remote_idp:
- source:
url: https://idp.adfs-demo.mydomdain.com/FederationMetadata/2007-06/FederationMetadata.xml
mapping:
entity_id: IDP_ENTITY_ID
post_url: IDP_POST
redirect_url: IDP_REDIR
logout_url: IDP_LOGOUT
signing_cert_alias: idp-saml2-sig
encryption_cert_alias: idp-saml2-enc
key_store:
static_keys:
- name: session-unison
version: 3
- name: lastmile-oidc
version: 1
trusted_certificates: []
key_pairs:
create_keypair_template:
- name: ou
value: k8s
- name: o
value: Tremolo Security
- name: l
value: Alexandria
- name: st
value: Virginia
- name: c
value: US
keys:
- name: unison-tls
tls_secret_name: unison-tls-secret
import_into_ks: keypair
create_data:
sign_by_k8s_ca: false
server_name: openunison.openunison.svc.cluster.local
subject_alternative_names: []
key_size: 2048
ca_cert: true
- name: amq-client
tls_secret_name: orchestra-amq-client
import_into_ks: keypair
create_data:
sign_by_k8s_ca: false
server_name: amq-client
subject_alternative_names: []
key_size: 2048
ca_cert: true
- name: amq-server
tls_secret_name: orchestra-amq-server
inmport_into_ks: certificate
create_data:
sign_by_k8s_ca: false
server_name: amq.openunison.svc.cluster.local
subject_alternative_names: []
key_size: 2048
ca_cert: true
- name: unison-saml2-rp-sig
tls_secret_name: unison-saml2-rp-sig
import_into_ks: keypair
create_data:
sign_by_k8s_ca: false
server_name: unison-saml2-rp-sig
subject_alternative_names: []
key_size: 2048
ca_cert: false
run_sql: |-
# By: Ron Cordell - roncordell
# I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
# make sure you have UTF-8 collaction for best .NET interoperability
# CREATE DATABASE quartznet CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
IS_DURABLE BOOLEAN NOT NULL,
IS_NONCONCURRENT BOOLEAN NOT NULL,
IS_UPDATE_DATA BOOLEAN NOT NULL,
REQUESTS_RECOVERY BOOLEAN NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(19) NULL,
PREV_FIRE_TIME BIGINT(19) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(19) NOT NULL,
END_TIME BIGINT(19) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 BOOLEAN NULL,
BOOL_PROP_2 BOOLEAN NULL,
TIME_ZONE_ID VARCHAR(80) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(140) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(19) NOT NULL,
SCHED_TIME BIGINT(19) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT BOOLEAN NULL,
REQUESTS_RECOVERY BOOLEAN NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(19) NOT NULL,
CHECKIN_INTERVAL BIGINT(19) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
ENGINE=InnoDB;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
DROP TABLE IF EXISTS ACTIVEMQ_ACKS;
DROP TABLE IF EXISTS ACTIVEMQ_LOCK;
DROP TABLE IF EXISTS ACTIVEMQ_MSGS;
CREATE TABLE `ACTIVEMQ_ACKS` (
`CONTAINER` varchar(250) NOT NULL,
`SUB_DEST` varchar(250) DEFAULT NULL,
`CLIENT_ID` varchar(250) NOT NULL,
`SUB_NAME` varchar(250) NOT NULL,
`SELECTOR` varchar(250) DEFAULT NULL,
`LAST_ACKED_ID` bigint(20) DEFAULT NULL,
`PRIORITY` bigint(20) NOT NULL DEFAULT '5',
`XID` varchar(250) DEFAULT NULL,
PRIMARY KEY (`CONTAINER`,`CLIENT_ID`,`SUB_NAME`,`PRIORITY`),
KEY `ACTIVEMQ_ACKS_XIDX` (`XID`)
);
CREATE TABLE `ACTIVEMQ_LOCK` (
`ID` bigint(20) NOT NULL,
`TIME` bigint(20) DEFAULT NULL,
`BROKER_NAME` varchar(250) DEFAULT NULL,
PRIMARY KEY (`ID`)
);
CREATE TABLE `ACTIVEMQ_MSGS` (
`ID` bigint(20) NOT NULL,
`CONTAINER` varchar(250) NOT NULL,
`MSGID_PROD` varchar(250) DEFAULT NULL,
`MSGID_SEQ` bigint(20) DEFAULT NULL,
`EXPIRATION` bigint(20) DEFAULT NULL,
`MSG` mediumblob,
`PRIORITY` bigint(20) DEFAULT NULL,
`XID` varchar(250) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `ACTIVEMQ_MSGS_MIDX` (`MSGID_PROD`,`MSGID_SEQ`),
KEY `ACTIVEMQ_MSGS_CIDX` (`CONTAINER`),
KEY `ACTIVEMQ_MSGS_EIDX` (`EXPIRATION`),
KEY `ACTIVEMQ_MSGS_PIDX` (`PRIORITY`),
KEY `ACTIVEMQ_MSGS_XIDX` (`XID`)
);
commit;
Go through each line. Make sure to update for your domains, urls, etc. Pay close attention to the saml_remote_idp
section. The url
is the IdP you'll use for authentication. This will pull in all of the urls and certificates needed for OpenUnison to authenticate your users.
Once ready, create the CR in the openunison
project. Once created the operator will take over, creating the objects needed and kicking off a build of the Orchestra implementation pointed to in the CR.
Complete Integrate with your Identity Provider
Orchestra's metadata is published at /auth/forms/saml2_rp_metadata.jsp
off of your OU_HOST
configuration option. So if OU_HOST
is orchestra.apps.mydomain.com then your metadata URL is https://orchestra.apps.mydomain.com/auth/forms/saml2_rp_metadata.jsp
. Import it into your identity provider and add the following attributes to the assertion so OpenUnison knows who the logged in user is:
Attribute Name | Active Directory Attribute | Description |
---|---|---|
uid | samAccountName | User's login id |
givenName | givenName | User's first name |
sn | sn | User's last name |
User's email address |
If using Active Directory Federation Services, you can use the following claims transformation rule:
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
=> issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "uid", "givenName", "sn", "mail"), query = ";sAMAccountName,sAMAccountName,givenName,sn,mail;{0}", param = c.Value);
Once the metadata is imported and the attributes are added, you are ready to login to Orchestra.
Create First Administrator
The user you logged in as is currently unprivileged. In order for other users to login and begin requesting access to projects this first user must be enabled as an approver. Login to the MySQL database deployed for Orchestra and execute the following SQL:
insert into userGroups (userId,groupId) values (2,1);
This will add the administrator group to your user. Logout of OpenUnison and log back in.
Self Request & Approve Cluster Administrator
Once SSO is enabled in the next step, you'll need a cluster administrator to be able to perform cluster level operations:
- Login to Orchestra
- Click on "Request Access" in the title bar
- Click on "OpenShift Administration"
- Click "Add To Cart" next to "Cluster Administrator"
- Next to "Check Out" in the title bar you'll see a red
1
, click on "Check Out" - For "Supply Reason", give a reason like "Initial user" and click "Submit Request"
- Since you are the only approver refresh OpenUnison, you will see a red
1
next to "Open Approvals". Click on "Open Approvals" - Click "Review" next to your email address
- Specify "Initial user" for the "Justification" and click "Approve"
- Click on "Confirm Approval"
At this point you will be provisioned to the cluster-admins
group in OpenShift that has a RoleBinding to the cluster-admin
Role. Logout of Orchestra and log back in. If you click on your email address in the upper left, you'll see that you have the Role OpenShift - cluster-admins
.
SSO with OpenShift
- Login to OCP4
- Go to
Administration
->Cluster Settings
->Global Configuration
->OAth
- Under
Identity Providers
clickAdd
and chooseOpenID Connect
Use this table to fill in the responses:
Option | Value |
---|---|
ClientID | openshift |
Client Secret | The base64 decoded value of OU_OIDC_OPENSHIFT_SECRET in your source secret |
Issuer URL | https://orchestra.apps.mydomain.com/auth/idp/OpenShiftIdP *Make sure to replace apps.mydomain.com with your own domain` |
Leave the defaults for everything else and click "Add"
It will take a few minutes, but when you logout and try to login again you'll be presented with an option to login via openunison
.