OpenShift에서 Identity Provider(a.k.a IDP)를 연동하여, 로그인시 통합인증(Single Sign On)을 구현하는 방법에 대해서 작성한다.
제공하는 Identity Provider가 많지만 현업에서 자주 사용하는 방식은 htpasswd, LDAP, OpenID가 있다.
이 3가지 방식을 기준으로 PoC 환경을 만들고 이를 검증하는 방식으로 진행 한다.
웹 기반의 인증 방식을 구현할때 사용하는 방식 중에 하나이다.
이 인증 방식은 Apache 웹 서버에서 mod_auth_basic 모듈을 사용하여 HTTP 인증 기능(Basic Authentication)을 구현할때,
htpasswd를 사용하여 사용자 계정을 생성하고 파일 기반의 데이터베이스를 사용한다.
(기본적으로 암호화 알고리즘은 MD5를 사용하며, Linux에서는 crypt() 함수를 사용하여 생성한다.)
다음 절차에서는 htpasswd를 사용하여 사용자 생성 및 OpenShift에 IDP를 구성하는 방법에 대해서 설명한다.
사용자 계정을 2개이상 생성 한다.
-c: 파일이 없는 경우 새로 생성 한다.
-b: 사용자 계정의 암호를 생성시 사용 된다.
[root@bastion ~]# mkdir -p /opt/idp/htpasswd/
[root@bastion ~]# htpasswd -cb /opt/idp/htpasswd/file.db admin 'Test!@#'
[root@bastion ~]# htpasswd -b /opt/idp/htpasswd/file.db ybkim 'Test!@#'
[root@bastion ~]# cat /opt/idp/htpasswd/file.db
admin:$apr1$30oZbRcG$9QqWyT4pVadMSd3RlPh3c.
ybkim:$apr1$K0Wz9TVd$3zs6Xjnf.LLtJDJZrwSWU/
htpasswd를 통해 생성한 사용자 계정 정보를 secret로 생성한다.
생성시 계정 정보는 base64 암호화 알고리즘으로 한번 더 암호화 되어 생성 된다.
[root@bastion ~]# oc create secret generic idp-htpasswd-secret \
--from-file=htpasswd=/opt/idp/htpasswd/file.db -n openshift-config
[root@bastion ~]# oc get secret idp-htpasswd-secret -n openshift-config -o yaml
apiVersion: v1
data:
htpasswd: YWRtaW46JGFwcjEkZUZzV3dNMjMkLzJta3NSSndma0lHLgjH2m5c8emE66pjdExmgep47BAdKTrsso2Vu8Ke6GEY5W51wwPPMqKZJowXQCead3BhTFQxCg==
kind: Secret
metadata:
name: idp-htpasswd-secret
namespace: openshift-config
type: Opaque
OAuth 설정에 htpasswd 방식의 IDP를 추가 정의하여 반영 한다.
[root@bastion ~]# vi /opt/idp/htpasswd/idp-htpasswd-oauth.yaml
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
name: cluster
spec:
identityProviders:
- name: idp-htpasswd
type: HTPasswd
mappingMethod: claim
htpasswd:
fileData:
name: idp-htpasswd-secret
기존 OAuth가 OpenShift에 구성되어 있기 때문에 create가 아닌 replace로 YAML 내용을 반영한다.
[root@bastion ~]# oc replace -f /opt/idp/htpasswd/idp-htpasswd-oauth.yaml
위 HTPasswd 방식의 IDP가 반영되기 위해 openshift-authentication namespace의 oauth-openshift Pod가 재배포 된다.
재배포가 정상적으로 수행 되면, Web Console에 접근시 아래와 같이 idp-htpasswd 이름의 로그인 버튼이 생성 된다.
관리를 위한 admin 계정에 최고 관리자 권한인 cluster-admin Role을 부여를 한다.
[root@bastion ~]# oc adm policy add-cluster-role-to-user cluster-admin admin
권한 부여 후 전에는 안보였던 Overview 화면을 볼 수 있게 되었다.
이는, 모든 namespace에 접근 권한을 부여 했기 때문이다.
일반 계정은 프로젝트(namespace)를 생성 후 해당 프로젝트에서만 관리 할 수 있도록 admin Role 권한을 부여한다.
[root@bastion ~]# oc new-project ybkim-project
[root@bastion ~]# oc adm policy add-role-to-user admin user1 -n ybkim-project
일반 계정에서는 특정 프로젝트(ybkim-project)에서만 사용이 가능 한 것으로 확인 되었다.
LDAP(Lightweight Directory Access Protocol)는 인터넷 기반의 분산 디렉터리 서비스를 제공하는 공개된 표준 프로토콜이다.
LDAP는 DAP(Directory Access Protocol)의 인터넷 프로토콜 통신 부분만 따로 분리해서 경량화로 설계 되었기 때문에,
통신 데이터를 적게 주고 받을수 있는 장점이 있다.
다음 절차에서는 현업에서 많이 사용하는 Microsoft Active Directory(a.k.a MS AD)를 기준으로 작성한다.
OS: Windows Server 2019 Datacenter Edition
SW: Microsoft Active Directory
Domain: ad.ocp4.ybkim.local
AD의 하위 디렉토리 구조는 아래와 같이 Tree 형태로 구성한다.
본 구성도는 ocp4.ybkim.local 도메인을 사용하는 회사에서 지사별(us, kr)로 구분하여 사용자를 별도 분리 시킨 구성이다.
이는 각각의 지사에서 OpenShift 클러스터가 별도로 존재하고 지사 별로 각기 다른 정책이 있다는 가정하에 설계 되었다.
MS Active Directory의 Administrator의 계정 암호를 secret로 생성한다.
생성시 계정 암호는 base64 암호화 알고리즘으로 한번 더 암호화 되어 생성 된다.
[root@bastion ~]# oc create secret generic idp-ms-ad-secret \
--from-literal=bindPassword='Test!@#' -n openshift-config
OAuth를 구성하기 위해 YAML 파일을 생성한다.
아래 URL 부분에 있는 baseDN의 범위는 한국지사(kr)로 지정하여 사용자를 조회 할 수 있도록 한다.
[root@bastion ~]# vi /opt/idp/ms-ad/idp-ms-ad-oauth.yaml
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
name: cluster
spec:
identityProviders:
- name: "idp-ms-ad"
type: LDAP
mappingMethod: claim
ldap:
attributes:
id:
- dn
email:
- mail
name:
- cn
preferredUsername:
- uid
bindDN: >-
cn=Administrator,cn=Users,dc=ocp4,dc=ybkim,dc=local
bindPassword:
name: idp-ms-ad-secret
insecure: true
url: >-
ldap://ad.ocp4.ybkim.local/ou=kr,dc=ocp4,dc=ybkim,dc=local?sAMAccountName
기존 OAuth가 OpenShift에 구성되어 있기 때문에 create가 아닌 replace로 YAML 내용을 반영한다.
[root@bastion ~]# oc replace -f /opt/idp/ms-ad/idp-ms-ad-oauth.yaml
위 YAML에서 주요 부분만 설명한다.
- bindDN
MS Active Directory에서 사용자 계정 조회가 가능한 관리자 계정을 지정한다.
- bindPassword
bindDN의 관리자 계정 암호를 secret key 파일명으로 지정한다.
- insecure
MS Active Directory의 인증서를 사용할지 여부이다.
true는 인증서를 사용하지 않으며, false는 인증서를 사용한다.
- url
LDAP의 사용자를 조회할 기본 디렉토리의 URL을 정의한다.
LDAP의 기본 포트는 389이며, 기본 포트를 변경하여 사용하고 있다면 URL에 해당 포트를 포함 해주면 된다.
ex) ldap://ad.ocp4.ybkim.local:3899/ou=kr,dc=ocp4,dc=ybkim,dc=local?sAMAccountName
사용자 조회 기준값은 sAMAccountName으로 구분한다.
위 MS AD 방식의 IDP가 반영되기 위해 openshift-authentication namespace의 oauth-openshift Pod가 재배포가 된다.
재배포가 정상적으로 수행 되면, Web Console에 접근시 아래와 같이 idp-ms-ad 이름의 로그인 버튼이 생성 된다.
관리를 위한 kr-admin 계정에 최고 관리자 권한인 cluster-admin Role을 부여를 한다.
권한을 주기 앞서 OpenShift의 Web Console로 MS Active Directory IDP로 최초 로그인을 하면, 아래와 같이 FULL NAME과 IDENTITIES에 해당 로그인 내역이 존재하는 것을 확인 가능하다.
[root@bastion ~]# oc get users
NAME UID FULL NAME IDENTITIES
Q049a1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiREM9b2NwNCxEQz15YmtpbSxEQz1sb2NhbA 5d495790-3bb2-41e9-8696-5b7ca47dce38 kr-admin idp-ms-ad:Q049a1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiREM9b2NwNCxEQz15YmtpbSxEQz1sb2NhbA
MS Active Directory에서 Role 권한을 주는 경우 CLI로는 권한을 주기 어렵다.
해서, YAML 파일을 생성하여 Role 권한을 반영 하도록 한다.
위의 고유 NAME 값으로 모든 namepsace에 접근 가능하도록 cluster-admin 권한을 부여 한다.
[root@bastion ~]# vi /opt/idp/ms-ad/idp-ms-ad-cluster-admin-role-binding.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cluster-admin-kr-admin
subjects:
- kind: User
apiGroup: rbac.authorization.k8s.io
name: Q049a1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiREM9b2NwNCxEQz15YmtpbSxEQz1sb2NhbA
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
[root@bastion ~]# oc create -f /opt/idp/ms-ad/idp-ms-ad-cluster-admin-role-binding.yaml
권한 부여 후 전에는 안보였던 Overview 화면을 볼 수 있게 되었다.
이는, 모든 namespace에 접근 권한을 부여 했기 때문이다.
일반 계정은 프로젝트(namespace)를 생성 후 해당 프로젝트에만 관리 할 수 있도록 admin Role 권한을 부여한다.
[root@bastion ~]# oc new-project ad-ybkim-project
[root@bastion ~]# oc get users
NAME UID FULL NAME IDENTITIES
Q049a3IteWJraW0sT1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiQz15YmtpbSxEQz1sb2NhbA 3c2a41ff-1117-44b1-ab18-b9f419dae3c7 kr-ybkim idp-ms-ad:Q049a3IteWJraW0sT1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiQz15YmtpbSxEQz1sb2NhbA
위의 고유 NAME 값으로 ad-ybkim-project의 namepsace에만 접근 가능하도록 admin 권한을 부여 한다.
[root@bastion ~]# vi /opt/idp/ms-ad/idp-ms-ad-admin-role-binding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: role-admin-kr-ybkim
namespace: ad-ybkim-project
subjects:
- kind: User
apiGroup: rbac.authorization.k8s.io
name: Q049a3IteWJraW0sT1GV9Jm2u7rmsCe65wKzPTw5jtS38n2tVEGiQz15YmtpbSxEQz1sb2NhbA
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin
[root@bastion ~]# oc create -f /opt/idp/ms-ad/idp-ms-ad-admin-role-binding.yaml
일반 계정에서는 특정 프로젝트(ad-ybkim-project)에서만 사용이 가능 한 것으로 확인 되었다.
OpenID는 사용자 계정을 한번의 로그인으로 여러 서비스를 일일이 가입할 필요 없이,
사용할 수 있도록 해주는 인증 서비스 표준이다.
이 인증방식은 통합인증 시스템(Single Sign On, SSO)이라고 부르며,
포털 사이트나 SNS(Social Networking Service) 같은 곳에서 많이 활용 한다.
이 문서에서는 keycloak을 사용하여 SSO 시스템을 OpenShift의 IDP와 연동하여 구현하도록 한다.
keycloak은 WildFly 기반으로 구성되어 있으며, RedHat에서 개발한 SSO 오픈소스이다.
(상용 제품은 RedHat SSO이고, 기반은 JBoss이다.)
또한, 기본 데이터베이스를 In-Memory 기반의 H2 Database를 사용하며,
Production 레벨의 환경에서는 비권장이고, RDBMS(PostgeSQL, MySQL, MariaDB, Oracle) 등과 같은
Database와 연동하여 사용 할 것을 권장한다.
https://www.keycloak.org/downloads 접속하여 파일을 다운로드 받는다.
[root@bastion ~]# curl -o '/opt/keycloak-13.0.0.tar.gz' -L 'https://github.com/keycloak/keycloak/releases/download/13.0.0/keycloak-13.0.0.tar.gz'
[root@bastion ~]# tar -xzvf /opt/keycloak-13.0.0.tar.gz -C /opt/
[root@bastion ~]# mv /opt/keycloak-13.0.0 /opt/keycloak
Keycloak 기본 홈 디렉토리를 설정 한다.
[root@bastion ~]# vi /etc/profile
# Keycloak
export KEYCLOAK_HOME="/opt/keycloak"
export PATH=$PATH:$KEYCLOAK_HOME/bin
OpenShift에서 OpenID 기반으로 IDP를 연동하는 경우에는 HTTPS 방식만 지원하기 때문에,
SSL 인증서 기반으로 keycloak 서버를 구동 할 수 있도록 작업이 진행 되어야 한다.
본 작업은 Self Signed 인증서 기반으로 생성부터 OpenShift와 keycloak에 적용까지의 내용을 담고 있다.
실제 Production 환경과는 다를수 있음을 이해 한다.
[root@bastion ~]# mkdir -p /opt/idp/certs/{root,router,conf}
[root@bastion ~]# openssl genrsa -aes256 -out /opt/idp/certs/root/rootca.key -passout pass:'Test!@#' 2048
[root@bastion ~]# vi /opt/idp/certs/conf/openssl-rootca.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = req_ext
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
MinProtocol = TLSv1.2
[req_distinguished_name]
countryName = "KR"
stateOrProvinceName = "Seoul"
localityName = "Gangnam-gu"
organizationalUnitName = "OpenShift"
commonName = "OpenShift - Self Signed RootCA"
[req_ext]
basicConstraints = critical,CA:TRUE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
[root@bastion ~]# openssl req -new \
-key /opt/idp/certs/root/rootca.key \
-out /opt/idp/certs/root/rootca.csr \
-passin pass:'Test!@#' \
-config /opt/idp/certs/conf/openssl-rootca.conf
[root@bastion ~]# openssl x509 -req -sha256 -days 36500 -extensions req_ext -set_serial 1 \
-in /opt/idp/certs/root/rootca.csr \
-signkey /opt/idp/certs/root/rootca.key \
-out /opt/idp/certs/root/rootca.crt \
-extfile /opt/idp/certs/conf/openssl-rootca.conf \
-passin pass:'Test!@#'
[root@bastion ~]# keytool -import -file /opt/idp/certs/root/rootca.crt -keystore /opt/idp/certs/root/rootca.truststore -keypass 'Test!@#' -storepass 'Test!@#'
Owner: CN=Self Signed Root CA, OU=OpenShift, L=Gangnam-gu, ST=Seoul, C=KR
Issuer: CN=Self Signed Root CA, OU=OpenShift, L=Gangnam-gu, ST=Seoul, C=KR
Serial number: 1
Valid from: Fri May 21 17:57:21 KST 2021 until: Sun Apr 27 17:57:21 KST 2121
Certificate fingerprints:
SHA1: 97:1B:24:A7:43:FC:87:B5:F2:0E:23:75:E1:43:93:6D:D9:B9:FD:3C
SHA256: E0:B7:BE:23:C0:79:A1:01:44:F3:D7:5A:A6:CE:24:CB:82:DA:1E:6F:99:6C:32:8A:F8:F6:64:B0:AB:4D:A8:C5
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key (3)
Version: {10}
Extensions:
#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
#2: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
serverAuth
]
#3: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_Encipherment
]
Trust this certificate? [no]: yes
Certificate was added to keystore
[root@bastion ~]# openssl genrsa -aes256 -out /opt/idp/certs/router/router.key -passout pass:'Test!@#' 2048;
[root@bastion ~]#cp /opt/idp/certs/router/router.key /opt/idp/certs/router/router.enc
[root@bastion ~]#openssl rsa -in /opt/idp/certs/router/router.enc -out /opt/idp/certs/router/router.key -passin pass:'Test!@#';
[root@bastion ~]# vi /opt/idp/certs/conf/openssl-router.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = req_ext
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
MinProtocol = TLSv1.2
[req_distinguished_name]
countryName = "KR"
stateOrProvinceName = "Seoul"
localityName = "Gangnam-gu"
organizationalUnitName = "OpenShift"
commonName = "*.apps.ocp4.ybkim.local"
[req_ext]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = "*.apps.ocp4.ybkim.local"
[root@bastion ~]# openssl req -new \
-key /opt/idp/certs/router/router.key \
-out /opt/idp/certs/router/router.csr \
-passin pass:'Test!@#' \
-config /opt/idp/certs/conf/openssl-router.conf
[root@bastion ~]# openssl x509 -req -days 36500 \
-in /opt/idp/certs/router/router.csr \
-CA /opt/idp/certs/root/rootca.crt \
-CAkey /opt/idp/certs/root/rootca.key \
-CAcreateserial -out /opt/idp/certs/router/router.crt \
-extensions req_ext \
-passin pass:'Test!@#' \
-extfile /opt/idp/certs/conf/openssl-router.conf
router 인증서를 pkcs12 포맷 형태로 변환 한다.
[root@bastion ~]# openssl pkcs12 -export \
-in /opt/idp/certs/router/router.crt \
-inkey /opt/idp/certs/router/router.key \
-out /opt/idp/certs/router/router.p12 \
-name router-keystore \
-password pass:'Test!@#' \
-CAfile /opt/idp/certs/root/rootca.crt
[root@bastion ~]# keytool -importkeystore \
-deststorepass 'Test!@#' \
-destkeypass 'Test!@#' \
-destkeystore /opt/idp/certs/router/router.keystore \
-srckeystore /opt/idp/certs/router/router.p12 \
-srcstoretype pkcs12 \
-srcstorepass 'Test!@#'
keycloak에서 사용 될 keystore 인증서 파일을 복사한다.
[root@bastion ~]# cp /opt/idp/certs/root/rootca.truststore $KEYCLOAK_HOME/standalone/configuration/
[root@bastion ~]# cp /opt/idp/certs/router/router.keystore $KEYCLOAK_HOME/standalone/configuration/
keycloack 구동시 모든 인터페이스에 서버가 바인딩 될 수 있도록 설정 한다.
[root@bastion ~]# sed -i 's/127.0.0.1/0.0.0.0/g' $KEYCLOAK_HOME/standalone/configuration/standalone.xml
security-realm 부분의 ApplicationRealm을 아래와 같이 keystore, truststore 부분을 추가/수정한다.
[root@bastion ~]# vi $KEYCLOAK_HOME/standalone/configuration/standalone.xml
<management>
<security-realms>
<security-realm name="ApplicationRealm">
<server-identities>
<ssl>
<!--
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
-->
<!-- Router Keystore -->
<keystore path="router.keystore" relative-to="jboss.server.config.dir" keystore-password="Test!@#" alias="router-keystore" key-password="Test!@#"/>
</ssl>
</server-identities>
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
</authorization>
<!-- RootCA Truststore -->
<authentication>
<truststore path="rootca.truststore" relative-to="jboss.server.config.dir" keystore-password="Test!@#" />
</authentication>
</security-realm>
</security-realms>
..........
..................
<subsystem xmlns="urn:jboss:domain:undertow:12.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.sta$
<buffer-cache name="default"/>
<server name="default-server">
<!-- <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true""/> -->
<!-- Add verify-client -->
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true" verify-client="REQUESTED"/>
</subsystem>
처음 구동시 관리자 계정이 없기 때문에, admin 계정을 하나 추가 한다.
[root@bastion ~]# add-user-keycloak.sh --user admin --password 'Test!@#'
Added 'admin' to '/opt/keycloak/standalone/configuration/keycloak-add-user.json', restart server to load user
background 방식으로 스크립트를 실행하여 서버를 구동한다.
[root@bastion ~]# /opt/keycloak/bin/standalone.sh > /dev/null 2>&1 &
keycloack의 기본 포트는 HTTP(TCP/8080), HTTPS(TCP/8443)이므로 OS단의 방화벽에서 해당 포트를 오픈 해준다.
[root@bastion ~]# firewall-cmd --permanent --zone=public --add-port=8080/tcp
[root@bastion ~]# firewall-cmd --permanent --zone=public --add-port=8443/tcp
[root@bastion ~]# firewall-cmd --reload
Keycloak의 Web Console에 로그인 후 Realm을 추가 한다.
Administration Console을 누른 후 admin / Test!@# 관리자 사용자 계정을 통해 로그인 한다.
Master Realm에서 "Add realm"을 추가 한다.
추가할 Realm의 이름을 "openshift"로 입력 한다.
추가 된 Realm인 "openshift"에서 좌측 "Client"를 누른 후 우측 "Create"를 눌러 생성 한다.
새로 생성할 "ClientID"의 이름을 "sso"라 입력 한다.
새로 생성한 ClientID인 "sso"에서 Access Type을 "confidential"로 선택 한다.
Vaild Redirect URIs는 OpenShift의 OAuth 주소를 적는다.
ex) https://oauth-openshift.apps.ocp4.ybkim.local/*
이후 Credentials 메뉴에서 Secret key를 기억 한다.
좌측 하단 부분에 위치하는 "Users"를 선택하여 우측 "Add user"를 선택 한다.
이후 Credentials 메뉴에서 Password를 지정한다.
Temporay는 기본값으로 활성화 되어 있으며, 이는 임시 Password로 설정되고 최초 로그인시 Password를 변경할 수 있도록 한다.
Temporay를 비활성화(OFF)를 하는 경우 Password 변경 없이 현재 지정한 Password로 사용할 수 있다.
Password를 입력하고 "Set Password" 버튼을 누르면 최종 설정 된다.
같은 방법으로 sso-ybkim 이라는 사용자 계정을 추가한다.
사용자 추가 이후 "View all users"를 누르면 위에 추가 한 사용자 목록을 확인 할 수 있다.
Router Pod의 기본 인증서는 ingress-operator가 2년이 되기 전에 인증서를 재갱신 한다.
이렇게 되면, 매 2년마다 서비스 장애가 발생 되는 원인이며, 이를 방지 하고자 Self Signed 인증서를 반영한다.
또한, keycloak에서 사용하는 keystore 인증서는 Self Signed 인증서를 기반으로 만들어졌기 때문에,
OpenShift에서 *.apps.ocp4.ybkim.local 도메인에 포함 된 keycloak 인증서를 자동 trust 하도록 한다.
위에서 생성한 router.crt, router.key 인증서를 tls.crt, tls.key 이름으로 복사 한다.
[root@bastion ~]# cd /opt/idp/certs/router/
[root@bastion router]# cp router.crt tls.crt
[root@bastion router]# cp router.key tls.key
인증서를 tls.crt, tls.key 이름으로 secret 저장소에 생성한다.
[root@bastion router]# oc create secret tls router-100y-cert --cert=tls.crt --key=tls.key -n openshift-ingress
Router 인증서를 신뢰하는 인증서로 등록 하도록 ca-bundle.crt에 인증서를 추가 등록 한다.
[root@bastion router]# oc create configmap router-100y-cert --from-file=ca-bundle.crt=tls.crt -n openshift-config
추가 한 Router 인증서를 모든 서비스에 사용 하도록 설정 한다.
[root@bastion router]# oc patch proxy/cluster \
--patch='{"spec":{"trustedCA":{"name":"router-100y-cert"}}}' --type=merge
Router 인증서를 실제로 Router Pod에 마운트하여 사용하기 위한 작업을 진행 한다.
[root@bastion router]# oc patch ingresscontroller/default \
--patch '{"spec":{"defaultCertificate":{"name":"router-100y-cert"}}}' \
--type=merge -n openshift-ingress-operator
인증서가 반영 되기까지 시간이 꽤 많이 소요 되므로 정상화 될때까지 기다린다.
Keycloak ClientID의 Credentials Secret Key를 Secret로 생성한다.
생성시 Secret key는 base64 암호화 알고리즘으로 한번 더 암호화 되어 생성 된다.
[root@bastion ~]# oc create secret generic idp-openid-secret \
--from-literal=clientSecret='d65adf01-cb7e-40b0-b159-c47af185e51f' -n openshift-config
OpenID 기반의 YAML 파일을 생성한다.
[root@bastion ~]# vi /opt/idp/openid/idp-openid-oauth.yaml
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
name: cluster
spec:
identityProviders:
- name: idp-openid
type: OpenID
mappingMethod: claim
openID:
claims:
email:
- email
name:
- name
preferredUsername:
- preferred_username
clientID: sso
clientSecret:
name: idp-openid-secret
extraScopes: []
issuer: 'https://keylocak.apps.ocp4.ybkim.local:8443/auth/realms/openshift'
위 YAML에서 주요 부분만 설명한다.
- clientID
Keycloak에서 생성한 Realm의 ClientID를 입력한다.
- clientSecret
Keycloak에서 생성한 Realm의 ClientID의 Secret key이다.
OpenShift에서 secret key로 업로드한 이름으로 입력한다.
- issuer
Keycloak에서 생성한 Realm의 이름이며,
OpenID로 로그인 행위시 Token 값을 얻기 위한 주소이다.
기존 OAuth가 OpenShift에 구성되어 있기 때문에 create가 아닌 replace로 YAML 내용을 반영한다.
[root@bastion ~]# oc replace -f /opt/idp/openid/idp-openid-oauth.yaml
위 OpenID 방식의 IDP가 반영되기 위해 openshift-authentication namespace의 oauth-openshift Pod가 재배포 된다.
재배포가 정상적으로 수행 되면, Web Console에 접근시 아래와 같이 idp-openid 이름의 로그인 버튼이 생성 된다.
로그인 화면이 keycloak 화면으로 redirect 되는 것을 확인 가능하다.
keycloak에서 생성한 sso-admin 계정으로 접근 해본다.
관리를 위한 sso-admin 계정에 최고 관리자 권한인 cluster-admin Role을 부여를 한다.
[root@bastion ~]# oc adm policy add-cluster-role-to-user cluster-admin sso-admin
권한 부여 후 전에는 안보였던 Overview 화면을 볼 수 있게 되었다.
이는, 모든 namespace에 접근 권한을 부여 했기 때문이다.
일반 계정은 프로젝트(namespace)를 생성 후 해당 프로젝트에서만 관리 할 수 있도록 admin Role 권한을 부여한다.
[root@bastion ~]# oc new-project sso-ybkim-project
[root@bastion ~]# oc adm policy add-role-to-user admin sso-ybkim -n sso-ybkim-project
일반 계정에서는 특정 프로젝트(sso-ybkim-project)에서만 사용이 가능 한 것으로 확인 되었다.
모든 설정을 초기화 하고 싶을 경우 아래와 같은 방법으로 IDP를 초기화 할 수 있다.
[root@bastion ~]# vi /opt/idp/idp-init.yaml
apiVersion: config.openshift.io/v1
kind: OAuth
metadata:
name: cluster
spec: {}
[root@bastion ~]# oc replace -f /opt/idp/idp-init.yaml
[1]: Apache Docs - Authentication and Authorization
[2]: Apache Docs - htpasswd - Manage user files for basic authentication
[3]: OpenShift Docs - Sample LDAP Custom Resource
[4]: OpenShift Blog - Adding authentication to your Kubernetes Web applications with Keycloak
[5]: Keycloak Docs - Keycloak on Openshift
[6]: YouTube - Steve Konish: OpenShift 4.5 with Red Hat Single-Sign-On (SSO) 7.3
[7]: GitHub Gist - gyfoster: Instructions for enabling mutual SSL in Keycloak and WildFly