GS리테일 DX 블로그

Digital Transformation으로 고객 생활 가치의 이노베이션을 꿈꾸는 IT 사람들의 이야기

Cloud&Security

[SSO] 1편: 오픈소스 SSO를 왜 도입하였나요.

DarionKim 2022. 2. 7. 17:53

아침에 출근을 하여 여러 업무시스템에 로그인할 때 업무시스템마다 로그인해야 된다면 얼마나 불편할까요.

하나의 아이디 및 패스워드로 여러 업무시스템에 접근하고 싶을 것 입니다.

 

이때 필요한 것이 SSO, Single Sign On 인데요.

 

하나의 디바이스에서 단일 계정 로그인으로 이미 자격증명이 약속된 여러 서비스에 접근 할 수 있습니다.

*웹 환경에서는 하나의 웹브라우저를 하나의 디바이스로 인지하고 있습니다. 

 

임직원용 SSO를 오픈소스 SSO로 도입하여 운영중인데요. 이번에는 SSO와 관련된 이야기를 적어보려 합니다.

 

1.왜 도입하려고 했는지 ...

기존에 상용 LDAP과 연결 된 상용SSO를 8개의 내부시스템에 연동되어 임직원 전용으로 사용하고 있었는데 어려움이 있었습니다.

  • LDAP, SSO에 대한 유지보수의 어려움 (EOM) 
  • 비표준 SSO 방식으로 인한 확장성의 어려움
  • SSO 서버 노후화로 장애발생 시 신속한 장애대응 및 원인분석 미흡

이에 개선해보자고 공감되어 상용솔루션 및 오픈소스 SSO 업그레이드를 검토해보고 개선하기로 방향성을 수립했습니다.

 

2.상용솔루션 및 오픈소스 SSO  중 어느 것을 선택했는지 ...

SSO 기능검증 대상 솔루션은 오픈소스 3식, 상용솔루션 3식을 검토대상 후보로 선정하여, 기능검토 및 당사시스템 연동가능 여부를 중점으로 검토하여 Aerobase(오픈소스)를 적합 1순위로 선정하였습니다.

구분 Aerobase Keycloak WSO2 Idendity 상용솔루션 A 상용솔루션 B 상용솔루션 C
OIDC O O O X X X
멀티팩터 인증 O O O O O O
관리자 UI O O O O O O
OpenJDK 지원 O O O - - -
Identity Brokering O O O O O O
Middleware nginx, Wildfly Wildfly WSO2 Carbon Include
(Tomcat)
범용 웹서버 하드웨어타입
(DB+인증+관리)
설치 난이도 아주 쉬움 쉬움 보통 업체 설치 업체 설치 업체 설치
지원 DB PostgreSQL
MySQL
MSSQL
Oracle
PostgreSQL
MySQL
MSSQL
Oracle
PostgreSQL
MySQL
MSSQL
DB2
LDAP RDBMS 솔루션 자체 DB
후보 순위 1 2 3 4 4 4

*Aerobase는 Keycloark 기반의 솔루션이라 많은 기능이 추가되어 있고 설치가 보다 쉬워 유지보수에 용이하다고 판단하였습니다.

 

3.이해하고 있어야 되는 기본 개념들 ...

3.1 OAuth, OIDC, SAML 차이점

OIDC 와 SAML 이 연합 인증(federated authentication)을 위한 업계 표준인데 반해 OAuth는 보호된 리소스(어플리케이션이나 파일들)에 대한 권한을 인가(authorization)하기 위한 프레임워크라는 점입니다.

 

통합인증 : OIDC, SAML (통합 로그인)

리소스인증 : OAuth (API 권한 관리)

 

OIDC 는 OAuth 2.0 프로토콜을 기반으로 하며, ID Token 이라고 하는 JSON Web Token(JWT)을 사용하여 OAuth 2.0 이 기본으로 제공하지 않는 부분인 scopes 나 Endpoint Discovery 와 같은 영역을 표준화합니다.

OIDC 특히 사용자 인증에 집중하고 있으며 웹사이트나 모바일 앱의 사용자 로그인을 구현하기 위해 널리 사용됩니다.

 

SAML은 OAuth 와 독립적이며 인증을 위해 JWT 가 아닌 XML SAML 포맷의 메시지를 교환하는 방식을 사용합니다.

엔터프라이즈 고객이 단일 로그인을 사용하여 여러개의 어플리케이션에 로그인할 수 있도록 지원하는 데 보다 일반적으로 사용됩니다.

 

OIDC와 SAML은 사용자가 인증을 시작할 때 차이가 시작됩니다.

OIDC에서 사용자 로그인은 일반적으로 인증을 담당하는 리소스의 HTTP 주소입니다. 반면에 SAML은 사이트와 자격 증명 공급자 간의 명시 적 신뢰를 기반으로하므로 알 수없는 사이트의 자격 증명을받는 것이 일반적이지 않습니다.

 

저희 사례는 OIDC는 내부 서비스에서만 사용하고 SAML은 외부 서비스와 사용하고 있습니다.

 

3.2 세션(Session) - 서비스, SSO (무한루프 발생 주의)

SSO를 연결하는 클라이언트에서 자주하는 실수가 SSO 인증 성공만 하면 연동이 완료되었다는 착각을 합니다.

서비스의 세션은 서비스에서 관리해야 되는데 SSO 인증에 성공하고 서비스 세션을 관리하지 않으면 무한루프가 발생합니다.

 

무한루프가 발생하면 SSO쪽에서 엄청난 부하가 발생하는데 SSO 클라이언트에서는 인지하기 어렵습니다.

 

개발환경에서 모니터링 통해 정상 연동을 확인을 지원해야 하며 운영에서도 SSO 클라이언트 서비스 변경에 따라 발생 할 수 있기 때문에 주기적으로 모니터링해야 됩니다.

4.구축시 공유 할 만한 것들 ...

4.1 Custom SPI(Service Provider Interface) 적용

Aerobase(Keycloak 기반)는 사용자 지정 코드 없이도 대부분의 사용 사례를 처리할 수 있도록 설계되었지만,

때론 상황에 따라 사용자 목적에 맞게 변경하고 싶고 Keycloak에는 자체 Service Provider를 구현할 수 있는 여러 SPI가 있습니다.

 

4.1.1 HASH

암호화 방식 중에 SHA-256 방식이 필요했는데 기본적으로 탑재되지 않아 Keycloak Custom SPI 프로젝트를 만들고

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.gsshop</groupId>
  <artifactId>keycloak-server-spi-private-gsshop-credential</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  
  <properties>
		<keycloak.version>7.0.1</keycloak.version>
		<java.version>1.8</java.version>
		<junit-jupiter.version>5.6.0</junit-jupiter.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  
  <dependencies>
	  <dependency>
	      <groupId>org.keycloak</groupId>
	      <artifactId>keycloak-server-spi</artifactId>
	      <version>${keycloak.version}</version>
	      <scope>provided</scope>
	  </dependency>
	  <dependency>
	      <groupId>org.keycloak</groupId>
	      <artifactId>keycloak-server-spi-private</artifactId>
	      <version>${keycloak.version}</version>
	      <scope>provided</scope>
	  </dependency>
	  <dependency>
	      <groupId>org.keycloak</groupId>
	      <artifactId>keycloak-core</artifactId>
	      <version>${keycloak.version}</version>
	      <scope>provided</scope>
	  </dependency>

	  <dependency>
		  <groupId>org.junit.jupiter</groupId>
		  <artifactId>junit-jupiter-engine</artifactId>
		  <version>${junit-jupiter.version}</version>
		  <scope>test</scope>
	  </dependency>
	  <dependency>
		  <groupId>org.junit.jupiter</groupId>
		  <artifactId>junit-jupiter-api</artifactId>
		  <version>${junit-jupiter.version}</version>
		  <scope>test</scope>
	  </dependency>
  </dependencies>
  
  <build>
		<finalName>${project.artifactId}</finalName>
		<plugins>
			<plugin>
				<groupId>org.wildfly.plugins</groupId>
				<artifactId>wildfly-maven-plugin</artifactId>
				<version>2.1.0.Beta1</version>
				<configuration>
					<skip>false</skip>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

PassowordHashProvider를 SHA256 PasswordHashProvider를 상속받아 구현하였습니다.

배포는 빌드 시 배포를 하거나  배포본을 특정 위치로 복사하여 배포하였습니다. 

# 빌드 시 wildfly:deploy 옵션 추가하면 빌드 및 배포 동시 진행
$ git clone https://git.gsenext.com/51scrum/keycloak-server-spi-private-gsshop.git
$ cd keycloak-server-spi-private-gsshop/credential
$ mvn clean install wildfly:deploy
 
# jar 파일을 특정 위치로 복사하는 방식으로도 배포 가능
$ cp target/keycloak-server-spi-private-gsshop-credential.jar /var/opt/aerobase/aerobase-server/standalone/deployments/

배포가 완료되어 재기동하면 관리자 화면에서 확인 할 수 있습니다.

 

4.1.2 Authenticator

Aerobase의 기본 기능은 사용자 계정 잠김시 정확한 패스워드를 입력해야 계정 잠김 메세지가 노출되는데

패스워드가 맞지 않아도 로그인 시도 하는 계정이 잠겼을 경우 계정 잠김 메세지가 먼저 노출되도록 Authenticator를 상속받아 구현하였습니다.

*프로젝트 구성은 4.1.1 HASH와 동일하게 구성하였습니다. 

4.1.1 HASH와 동일한 방식으로 배포하게 되면 관리자 화면에서 확인 할 수 있습니다.

 

 

4.2 부하테스트

JMeter를 활용하여 특정 서비스 기준으로 기준으로 운영서버에 1,000명의 사용자에 대한 부하테스트를 실시하였습니다.

1,000개의 사용자를 생성하고 서버 모니터링 툴을 셋팅하고 네트워크 구성도를 공유하였습니다.

테스트 시나리오는 로그인 화면 접근, 로그인, 로그아웃으로 구성하여 진행하였습니다.

  1. SSO Login Access
    - 목적 : Get session & cookie
    - URL : login.xxxx.co.kr/auth/realms/xxxx/protocol/openid-connect/auth
    - Param(GET) : client_id, Response_type, redirect_url
    - Response :  
      .  url : login.xxxx.co.kr/auth/realms/xxxx/login-actions/authenticate?session&execution&client_id&tab_id
      . Response URL로 ID/PW 전송
  2. SSO Login
    - URL :  login.xxxx.co.kr/auth/realms/xxxx/login-actions/authenticate?session&execution&client_id&tab_id
      . "SSO Login Access"시에 발급받은 값으로 전송 session&execution&client_id&tab_id
    - Param(POST) : username, password
  3. SSO Logout
    - URL : login.xxxx.co.kr/auth/realms/xxxx/protocol/openid-connect/logout
    - "SSO Login Access"시에 부여받은 Cookie 값으로 해당 session 종료처리

목표 TPS는 최대부하시 50 TPS,  동접 100 user 기준으로 잡고 Jmenter로 Request/Response/TPS를 측정하고 APM으로 Server(Web/DB) CPU, Memory을 측정하였습니다.

 

결과값으로 수행시간(a), 처리량(b), 평균처리시간/건, TPS(초당커리건수 = b / a), 서버모니터링결과 비교하였습니다. 

 

5.다음 내용으로 ...

지금까지 오픈소스 SSO를 도입한 목적과 구축 과정을 이야기했습니다.

 

회사가 문제를 해결하기 위해서 다양한 기술을 함께 학습하면서 역량을 키우는 기술문화에서 배우는 것이 많다고 생각합니다.

동료분들하고 SSO를 접근하면서 다양한 개념과 원리를 접했고 예상하지 못한 이슈를 직면하면서 함께 해결하는 희열을 느꼈습니다.

 

함께 하는 것이 개발자에 있어 정말 중요하다고 생각합니다.

다음 내용으로

1)SSO Session Clustering 설정 및 트러블 슈팅

2)SSO와 M365 연동

 

을 준비하여 이야기하려고 합니다.

 

감사합니다.^^


김헌기 Darion | 뉴테크본부 > BO Product 부문 > 주문 Product 팀

Backend Office 주문 / 결제 / 마케팅프로모션 / 연계 / 혁신과제 서비스를 담당하고 있습니다.

동료들과 함께 기술 문화 만들기와 낯선 기술자와의 인연의 시작에 관심이 많습니다.


참고 및 출처