FINLOG
securitykeycloaklending

제로트러스트를 적용하려다 보니 왜 Keycloak을 선택하게 됐는가

지난 글에서는 금융권 보안이 왜 제로트러스트를 말하기 시작했는지 정리했다.
이번에는 그걸 조금 더 구현 쪽으로 가져와 보려고 한다.

관심사는 단순했다.

여신 시스템에서 보안을 적용한다고 할 때, 실제로 어디까지를 인증 서버가 맡고 어디부터는 서비스가 직접 판단해야 할까?

이 질문을 따라가다 보니 자연스럽게 Keycloak까지 연결됐다.

여신 시스템에서는 로그인만 붙인다고 끝나지 않는다

여신 시스템은 고객 정보, 계좌 정보, 대출 신청 정보, 상환 이력, 연체 상태 같은 데이터를 다룬다.
여기에 대출 심사, 승인, 실행, 상환처럼 실제 자산 흐름에 영향을 주는 기능까지 들어온다.

이런 시스템에서 "로그인한 사용자냐 아니냐"만 확인하는 건 출발점일 뿐이다.
실제로는 그 다음 질문들이 더 중요하다.

  • 이 요청자가 정말 해당 고객 본인인가
  • 이 운영자가 이 단계까지 볼 권한이 있는가
  • 이 계좌나 대출 건에 접근할 자격이 있는가
  • 지금 상태에서 실행, 상환, 연체 전환이 가능한가
  • 같은 요청이 중복으로 들어온 건 아닌가
  • 누가 어떤 작업을 했는지 나중에 추적 가능한가

즉 보안은 로그인 화면 하나로 끝나는 게 아니라, 요청이 서비스 안으로 들어온 이후 계속 이어진다.

그래서 인증과 도메인 판단을 분리하고 싶었다

처음에는 customer-service 안에 회원, 비밀번호, 로그인, 토큰 발급까지 다 넣는 것도 생각할 수 있다.
작게 시작한다는 명목으로는 그렇게 가기 쉽다.

그런데 조금만 생각해 보면 이 구조는 금방 책임이 무거워진다.

직접 구현해야 하는 것만 해도 꽤 많다.

  • 비밀번호 저장과 정책
  • 로그인 실패 처리
  • 계정 잠금 정책
  • 토큰 발급과 검증
  • 만료와 재발급
  • 운영자 계정 분리
  • MFA 확장
  • 권한 claim 관리

게다가 이건 한 번 만들고 끝나는 코드가 아니다.
나중에 고객 계정과 운영자 계정을 나누고, 운영자에 MFA를 붙이고, 서비스 계정까지 분리하려고 하면 인증 로직이 업무 서비스 안에서 계속 비대해진다.

그래서 여기서 판단한 건 이거였다.

인증 자체를 서비스 코드 안에 깊게 넣기보다, 인증 책임을 별도 서버로 분리하는 쪽이 낫다.

Keycloak은 정확히 어떤 역할을 하는가

Keycloak은 인증과 인가를 위한 서버다.
조금 더 실무적으로 말하면, 다음 같은 걸 맡길 수 있다.

  • 로그인 처리
  • 사용자 저장
  • 비밀번호 정책
  • 토큰 발급
  • 역할(role) 관리
  • 클라이언트(client) 관리
  • 운영자 MFA

그리고 애플리케이션 서비스들은 그 결과로 받은 토큰을 검증하는 쪽에 집중할 수 있다.

이걸 구조로 바꾸면 역할이 나뉜다.

Keycloak이 맡는 일

  • 고객 로그인
  • 운영자 로그인
  • JWT 발급
  • Realm, Client, Role 관리
  • 운영자 MFA 정책

서비스가 맡는 일

  • 토큰이 유효한지 확인
  • 이 사용자가 지금 요청한 자원에 접근 가능한지 판단
  • 고객 본인 여부 판단
  • 계좌 소유권 확인
  • 대출 상태 전이 검증
  • 감사로그 기록
  • 멱등성 검증

이 구분이 꽤 중요했다.
인증 서버가 있다고 해서 서비스의 보안 판단 책임이 사라지는 건 아니기 때문이다.

왜 직접 구현 대신 Keycloak을 선택했는가

이번에 정리하면서 스스로에게 제일 많이 했던 질문은 이거였다.

이걸 내가 꼭 직접 만들어야 하나?

결론은 아니었다.

이유는 세 가지였다.

1. 고객과 운영자 계정을 같은 수준으로 보면 안 됐다

고객 인증과 운영자 인증은 성격이 다르다.
운영자는 더 넓은 데이터와 더 위험한 기능에 접근하게 된다.

예를 들어 여신 시스템에서는 운영자가 다음 같은 기능에 접근할 수 있다.

  • 대출 심사
  • 승인
  • 실행
  • 배치 수동 실행
  • 연체 전환 관련 작업

이 영역은 고객 로그인보다 강한 통제가 필요하다.
운영자 역할 분리와 MFA를 처음부터 고려해야 했다.

Keycloak은 이걸 Realm, Client, Role, MFA 정책으로 비교적 자연스럽게 가져갈 수 있다.

2. 서비스는 인증보다 도메인 판단에 집중해야 했다

여신 시스템의 진짜 어려운 부분은 로그인 화면이 아니라 도메인 검증이다.

  • 대출 신청자가 본인인지
  • 이 계좌가 실제로 연결 가능한 상태인지
  • 현재 대출 상태에서 실행이 가능한지
  • 상환 요청이 중복인지
  • 연체 전환 조건이 충족됐는지

이런 건 customer-service, account-service, lending-service, batch-service 안에서 직접 판단해야 한다.

그런데 서비스 코드가 로그인 처리, 비밀번호 정책, 토큰 발급까지 같이 안고 가면 정작 중요한 도메인 검증이 흐려진다.
그래서 인증은 외부로 빼고, 서비스는 자기 보안 책임과 도메인 판단에 집중시키는 게 더 나았다.

3. 확장 포인트를 미리 열어두고 싶었다

초기에는 단순 로그인만 있어도 돌아갈 수 있다.
하지만 운영을 생각하면 곧 이런 요구가 생긴다.

  • 운영자 MFA
  • 서비스 계정 분리
  • client credentials 기반 내부 호출
  • role/scope 기반 접근 제어
  • 인증 정책 조정

이걸 전부 애플리케이션 서비스 안에서 직접 진화시키는 것보다, 인증 서버를 두고 그 위에서 정책을 가져가는 편이 구조적으로 더 안정적이었다.

제로트러스트 관점에서 Keycloak은 어디까지 해결해주는가

이 부분은 조금 선을 그어둘 필요가 있다.
Keycloak을 도입한다고 해서 제로트러스트가 자동으로 완성되는 건 아니다.

Keycloak이 잘하는 건 사용자와 클라이언트의 신원을 관리하고 토큰을 발급하는 일이다.
하지만 여신 시스템에서 필요한 제로트러스트 검증은 그보다 더 넓다.

예를 들면 이런 것들은 여전히 서비스가 직접 책임져야 한다.

  • 고객 본인 여부 확인
  • 특정 계좌 소유권 확인
  • 대출 상태 전이 검증
  • 내부 서비스 호출의 목적과 scope 검증
  • 멱등성 키 검증
  • 감사로그 적재

즉 Keycloak은 제로트러스트의 시작점이지, 종착점은 아니다.
내가 이해한 역할 분담은 이렇다.

  • Keycloak: 누구인가
  • 서비스: 그래서 무엇을 허용할 것인가

이 차이를 구분하는 게 생각보다 중요했다.

구현하면서 느낀 현실적인 포인트

문서로만 보면 Keycloak은 "붙이면 되는 인증 서버"처럼 보이기도 한다.
그런데 실제로 붙여보면 배운 게 꽤 많다.

1. 인증 서버를 고른다고 끝나지 않는다

Keycloak을 띄운다고 바로 끝나지 않았다.
MySQL 초기화, 기동 순서, healthcheck, 환경변수, 관리 포트 같은 운영 포인트가 생각보다 많이 따라왔다.

특히 처음에는 DB 초기화와 컨테이너 기동 순서 때문에 여러 번 다시 봤다.
이 과정에서 느낀 건, 인증도 결국 애플리케이션 기능이 아니라 운영 시스템이라는 점이었다.

2. 서비스가 gateway를 통과했다고 안심하면 안 된다

외부 요청은 gateway를 통해 들어오게 하더라도, 각 서비스는 토큰을 다시 검증해야 한다.
그리고 내부 서비스 호출도 "같은 시스템 안의 요청"이라고 자동 신뢰하면 안 된다.

결국 구조는 자연스럽게 이렇게 간다.

  • gateway에서 1차 검증
  • 각 서비스에서 다시 검증
  • 내부 호출도 별도 신원과 권한 확인

이건 Keycloak을 붙이면서 더 선명해졌다.

3. 보안은 한 번의 인증이 아니라 여러 번의 판단이다

이번에 제일 크게 남은 문장은 이거였다.

보안은 로그인 성공 여부가 아니라, 각 요청을 어디까지 허용할지 계속 판단하는 과정에 가깝다.

Keycloak은 그 판단에 필요한 신원 정보를 제공한다.
하지만 실제 허용과 차단은 서비스 안에서 더 많이 결정된다.

정리

여신 시스템에서는 인증을 붙였다는 말만으로는 보안을 설명하기 어렵다.
고객, 운영자, 내부 서비스가 각각 다른 수준의 권한과 통제를 필요로 하고, 실제 위험은 로그인 이후의 요청 처리 과정에서 더 많이 드러나기 때문이다.

그래서 이번에 내린 판단은 인증 책임과 도메인 판단 책임을 나누는 쪽이었다.
Keycloak은 로그인, 토큰 발급, 역할 관리, MFA 같은 인증 책임을 맡고, 서비스는 그 토큰을 바탕으로 실제 접근 가능 여부를 판단한다.

이 구조가 마음에 들었던 이유는 단순히 "남이 만든 인증 서버를 쓰자"가 아니라, 어떤 책임을 서비스에서 빼고 어떤 책임은 끝까지 서비스 안에 남겨야 하는지가 조금 더 분명해졌기 때문이다.

다음에는 여기서 한 걸음 더 들어가서, 내부 서비스 호출까지 검증 대상으로 보는 구조를 어떻게 가져갈지 정리해보려고 한다.