NATS의 JWT 기반 decentralized authentication/authorization
NATS 시스템을 운영하는데 필요한 권한에 대해서 다룬다.
용어 정리
Identity
Identity는 NATS의 인증/인가 체계에서 식별되고 권한을 부여받는 논리적 주체이다.
Operator - NATS 시스템의 Root of Trust가 되는 Identity
Account - 멀티테넌시와 권한 경계를 구성하는 Identity
User - 실제 Client가 인증할 때 사용하는 Identity
NKey
NATS가 사용하는 키 체계 이름
NKey 구성 요소
Public key(Public NKey)
다른 주체가 서명을 검증할 때 사용한다.
공개해도 된다.
Private seed(Private NKey)
실제 서명을 생성할 때 사용하는 Private key 역할을 한다.
외부에 노출하면 안 된다.
NKey 타입
Operator NKey
Account NKey
User NKey
NKey 역할
NKey는 다음의 두 역할로 분류된다.
Identity NKey - Identity 자체를 식별하며, 하위 JWT를 직접 서명할 수도 있는 기본 NKey
Signing NKey - 하위 Identity를 서명하기 위한 NKey
NKey의 생성과 Identity 구성은 별개의 과정이다. Identity를 생성할 때 별도로 만들어 둔 NKey를 지정할 뿐이다.
JWT
JWT는 Identity에 대한 정보와 권한을 담고, 상위 Identity가 그 내용을 보증하기 위해 서명한 Claim 문서이다.
NATS에서 각 Identity의 JWT는 상위 Identity NKey 또는 Signing NKey로 서명된다. Operator는 상위 Identity가 없으므로 자신의 Identity NKey로 서명한다.
iss - Identity Public NKey 또는 Signing Public NKey, 어떤 NKey로 서명되었는지를 나타냄.
sub - 해당 JWT가 표현하는 Identity Public NKey.
signing_keys - 이 Identity를 대신에 하위 JWT를 서명할 수 있도록 위임된 Signing Public NKey 목록.
Trust Chain
NATS 시스템은 다음의 신뢰 체인을 구성한다.
Operator
│ signs
▼
Account JWT
│ signs
▼
User JWT
│
└── User NKey를 가진 클라이언트가 실제 접속각 과정은 다음과 같다.
Account 생성 시에 시스템에 Account의 Public key, Private key 생성을 요청한다.
JWT Claim을 구성하고 Operator JWT의 signing_keys에 포함되어 있는 NKey를 이용해 Account JWT를 서명한다.
Account JWT가 resolver에 등록된다.
User 생성은 서명참여자가 Operator가 아닌 Account로 바뀔 뿐 동일하다.
Operator
Operator는 상위 Identity가 존재하지 않는다. 따라서 외부의 신뢰할 수 있는 관리 환경에서 Operator Identity NKey를 생성하고, 그 키로 Operator JWT를 서명후 시스템에 등록하면서 시작한다.
즉, 최초의 신뢰는 시스템 실행시 서버가 구성하는 것이 아니고 운영자가 주입하는 것이다. 이 정보를 주입하는 파일이 server.conf이다.
Account
Operator가 NATS 시스템 전체의 Root 신뢰 Identity라면, Account는 실제 서비스에 권한과 정책이 적용되는 Identity이다.
하나의 NATS 시스템은 여러 Account를 가질 수 있으며 각 Account는 서로 독립적인 테넌트처럼 동작한다.
따라서 Account 정책은 NATS 시스템을 운영하면서 자주 변경된다. 하지만 NATS의 Decentralized Authentication에서는 Account의 상태를 JWT로 표현하는데, JWT는 한번 서명되면 내용을 수정할 수 없다는 문제를 가지고 있다.
NATS는 이것을 Resolver라는 계층을 도입해서 해결하고자 했다.
Resolver
Resolver의 역할을 단순하게 표현하면 다음과 같다.
Account Public NKey에 대한 JWT를 저장, 조회, 배포
Resolver는 Account에 대한 정책이 변경될 때마다 현재 상태의 JWT를 새롭게 배포하고, 이후에 JWT 신뢰성 검증을 위해 조회 기능을 제공한다.
User
앞서, Account는 정책에 대한 테넌시 역할을 한다고 했다. Account는 정책과 영역을 담당한다고 이해하면 된다. 이제 이 Account에 설정된 정책을 이용하는 User에 대해서 알아본다.
User는 실제 클라이언트가 NATS 시스템에 접속할 때 사용하는 최종 Identity이다.
Operator는 시스템의 신뢰를 구성하고, Account는 시스템의 정책을 구성하는 역할을 한다면 User는 그 정책 안에서 실제 기능을 수행하는 주체이다.
User는 Account와는 다르게 시스템에 등록되지 않는다. Client가 접속할 때 자신의 User JWT를 제출하고, 서버는 그때 해당 User가 신뢰 가능한 Identity인지 검증한다.
이것이 decentralized authentication의 중요한 특징 중 하나다.
User JWT에는 User의 권한이 들어있지만 JWT 자체는 비밀정보가 아니다. 따라서 User는 추가적인 인증 정보를 제공해야한다. 그것이 .creds 파일이다.
.creds
NATS Client가 User Identity로 접속하려면 User JWT와 User Seed가 모두 필요하다.
User JWT는 해당 User가 누구인지와 어떤 Account에 속해 있으며 어떤 권한을 가지는지를 표현하는 signed Claim이다.
하지만 User JWT 자체는 secret이 아니므로, JWT를 가지고 있다는 사실만으로는 현재 접속하려는 Client가 실제 User Identity의 소유자인지 증명할 수 없다.
이를 위해 NATS는 User Identity NKey의 Private Seed를 사용한다.
creds는 다음으로 구성된다
User JWT - User는 누구이며 어떤 권한을 가지는가?
User Seed(Private NKey) - 현재 클라이언트가 이 User Identity의 Private NKey를 실제로 소유하고 있는가?
Trust Chain Validation
앞에서는 Operator -> Account -> User 방향의 신뢰 관계가 어떻게 구성되는지 살펴보았다.
이번에는 반대로 클라이언트가 User Identity를 이용해 접속할 때, NATS 시스템이 User -> Account -> Operator 방향으로 신뢰 관계를 검증하는 과정을 살펴본다.
1. 클라이언트가 User Identity의 소유권을 증명한다.
클라이언트는 .creds 파일을 이용해 NATS 시스템에 접속한다. NATS 시스템은 연결 과정에서 일회성 challenge 값인 nonce를 클라이언트에게 전달한다.
클라이언트는 이 nonce를 message로 하여 User Seed로 서명하고 signature를 생성한 뒤, User JWT와 함께 NATS에 전달한다.
NATS는 User JWT의 sub에 들어 있는 User Public NKey를 이용해 signature를 검증한다.
Verify(nonce, signature, user public key)검증에 성공하면 현재 접속하는 클라이언트가 User JWT의 sub에 대응 하는 Private Seed를 가지고 있음을 확인할 수 있다.
2. User JWT가 Account에 의해 서명되었는지 검증한다.
클라이언트가 User Identity를 실제로 소유하고 있더라도 아직 User를 신뢰할 수 없다. 누구든지 User NKey와 그대 대응하는 JWT를 만들 수 있기 때문이다.
따라서 NATS 시스템은 다음 단계로, User JWT가 신뢰할 수 있는 Account에 의해 발급되었는지 검증한다.
Account Signing Nkey로 User JWT를 서명한 경우 User JWT에는 다음 정보가 존재한다.
iss - User JWT를 서명한 Account Signing Public NKey
issuer_account - User JWT가 속한 Account의 Identity Public NKey
sub - User Identity Public NKey
NATS 시스템은 issuer_account를 이용해 Resolver에서 Account JWT를 조회한다.
Account JWT는 Account Identity Public NKey를 나타내는 sub 필드와 Signing Public NKey 목록을 나타내는 signing_keys 필드가 존재한다. 두 필드 중 한 쪽이 User JWT 서명자인 iss 필드와 일치한다면 User JWT가 Account로부터 서명됨을 확인한다.
이 부분 다시 작성 필요
다음 단계로 서명에 대한 검증 단계가 남았다. User JWT를 발급할 때 Account는 JWT의 signing input인 encoded-header.encoded-payload를 자신의 Identity Seed 또는 Signing Seed로 서명하여 signature를 생성한다. 그 결과로 만들어진 JWT는 header.payload.signature다. 이
따라서 NATS는 User JWT에 포함된 header.payload, signature와 서명자의 Public NKey를 이용하여 해당 JWT가 실제 그 키의 소유자에 의해 서명되었는지 검증할 수 있다.
Verify(JWT header"."JWT payload, JWT signature, Account Signing Public NKey)