위임 토큰
위임 토큰 (Delegation Tokens)
이 문서는 Flink가 사용하는 위임 토큰(delegation token)을 설명하고 그 비밀을 풀기 위한 것입니다. 위임 토큰은 장기(long-lived) 자격증명을 대체하기 위해 일부 서비스가 사용하는 인증 토큰이며, 분산 애플리케이션에서 안전하게 배포·갱신됩니다.
출처: 문서
본문
이 문서는 Flink가 사용하는 위임 토큰을 설명하고 난해한 개념을 풀기 위한 것입니다. 세부 사항으로 들어가기 전에 먼저 고수준 아키텍처 다이어그램을 보겠습니다.
위임 토큰이란 무엇이며 왜 사용하는가? (What Are Delegation Tokens and Why Use Them?)
위임 토큰(이하 DT)은 장기 자격증명(long-lived credentials)을 대체하기 위해 일부 서비스가 사용하는 인증 토큰입니다. Hadoop 생태계의 많은 서비스가 DT를 지원하는데, 장기 자격증명에 비해 매우 바람직한 장점이 있기 때문입니다:
장기 자격증명을 배포할 필요가 없음
분산 애플리케이션에서 장기 자격증명의 배포는 까다롭습니다. 또한 모든 사용자가 애플리케이션 데이터의 일부로 네트워크를 통해 배포하기를 원하지 않습니다. 이는 악의적인 행위자에게 추가 공격 표면이 되기 때문입니다.
DT는 JobManager(이하 JM) 같은 단일 위치만 장기 자격증명을 요구하게 합니다. 그 엔터티는 DT를 분산 애플리케이션의 다른 부분(예: TaskManager, 이하 TM)에 배포하여 서비스에 인증하도록 할 수 있습니다.
서비스당 단일 토큰이 인증에 사용됨
Kerberos 인증을 사용한다면 서버에 대한 각 클라이언트 연결은 Key Distribution Center(KDC) 왕복과 서비스 티켓 생성을 요구합니다. 분산 시스템에서 서비스 티켓 수는 클라이언트 프로세스 수(예: TM) 곱하기 서비스 프로세스 수(예: HDFS DataNode)에 비례하여 빠르게 늘어날 수 있습니다. 이는 KDC에 불필요한 추가 부하를 만들고 KDC 관리자가 설정한 사용량 한도에 걸릴 수도 있습니다.
위임 토큰은 인증에만 사용됨
DT는 장기 자격증명과 달리 발급된 특정 서비스에만 인증하는 데만 사용할 수 있습니다. 기존 DT로 새 DT를 만들거나 다른 서비스용 DT를 만들 수는 없습니다.
간단히 말해 DT는 장기 자격증명이 아닙니다. 많은 서비스가 Kerberos 인증 또는 다른 형태의 인증을 대체하기 위해 사용하지만, 인증 메커니즘에 묶는 것은 (구현 세부 사항을 제외하면) 아무것도 없습니다.
위임 토큰의 수명주기 (Lifecycle of Delegation Tokens)
DT는 일부 장기 자격증명과 달리 서비스별입니다. 서비스용 DT를 만들기 위해 연락하는 중앙 위치는 없습니다. 따라서 DT를 얻는 첫 단계는 해당 서비스에 인증할 수 있는 것입니다. Hadoop 생태계에서 이는 일반적으로 Kerberos로 수행됩니다.
이를 위해서는 애플리케이션이 사용할 수 있는 장기 자격증명이 어딘가에 있어야 합니다. 사용자가 일반적으로 그러한 자격증명을 제공하며, 가장 흔하게는 KDC에 로그인(예: kinit 사용)하여 수행됩니다. 이는 티켓 부여 티켓(TGT)을 포함하는 "credential cache"를 생성하며, 이후 서비스 티켓을 요청하는 데 사용할 수 있습니다.
TGT를 얻는 다른 방법도 있지만 궁극적으로는 프로세스를 부트스트랩하려면 TGT가 필요합니다.
TGT를 사용할 수 있게 되면 대상 서비스의 클라이언트 라이브러리를 사용하여 서비스에 인증하고 위임 토큰 생성을 요청할 수 있습니다. 이 토큰은 이제 다른 프로세스에 보내져 해당 서비스에 속한 다른 데몬에 인증하는 데 사용될 수 있습니다. 그리고 여기서 DT의 첫 번째 단점이 드러납니다: 생성과 사용에 서비스별 로직이 필요합니다.
Flink는 (어느 정도) 플러그 가능한 내부 DT 생성 API를 구현합니다. 새 서비스 지원은 애플리케이션에 대한 위임 토큰을 생성할 때 위임 토큰 매니저(delegation token manager)가 호출하는 DelegationTokenProvider를 구현하여 추가할 수 있습니다.
일단 생성되면 DT가 동작하는 방식의 의미론도 서비스별입니다. 그러나 일반적으로 Kerberos 토큰의 의미론을 따릅니다:
- "갱신 가능 기간(renewable period)"(TGT의 "lifetime"과 동일)은 갱신이 필요해지기 전의 DT 유효 기간을 뜻합니다.
- "최대 수명(max lifetime)"(TGT의 "renewable life"와 동일)은 DT를 갱신할 수 있는 시점까지의 시간을 뜻합니다.
토큰이 "max lifetime"에 도달하면 적절한 서비스에 연락하여 새 토큰을 만들고 위의 과정을 다시 시작해야 합니다.
위임 토큰 갱신과 갱신자 (Delegation Token Renewal and Renewers)
이것은 DT 처리에서 가장 혼란스러운 부분이며, 그 이유 중 일부는 시스템의 많은 부분이 Apache Hadoop YARN을 염두에 두고 설계되었기 때문입니다(다른 서비스와 메커니즘으로 확장되지만).
위에서 보았듯이 DT는 마침내 영구히 만료될 때까지 주기적으로 갱신되어야 합니다. 예를 들어 HDFS 서비스의 기본 구성은: 위임 토큰은 최대 7일간 유효하며 24시간마다 갱신해야 합니다. 토큰이 갱신되지 않고 24시간이 지나면 토큰을 더 이상 사용할 수 없습니다. 그리고 7일 후에는 토큰을 더 이상 갱신할 수 없습니다.
이것은 질문을 제기합니다: 누가 토큰을 갱신하는가? 그리고 오랫동안 그 답은 YARN이었습니다.
YARN 애플리케이션이 제출되면 DT 세트도 함께 제출됩니다. YARN은 이 토큰들을 컨테이너에 배포(UserGroupInformation API가 정한 관례 사용)하고, 앱이 실행되는 동안 갱신된 상태로 유지합니다. 이 토큰들은 애플리케이션만 사용하는 것이 아니라 YARN 자신도 로그 수집·집계 같은 기능을 구현하는 데 사용합니다.
그러나 여기에는 몇 가지 주의점(caveats)이 있습니다.
누가 토큰을 갱신하는가?
YARN의 경우 이는 Hadoop 라이브러리가 대부분 투명하게 처리합니다. 일부 서비스에는 토큰 "renewer"라는 개념이 있습니다. 이 "renewer"는 DT를 갱신할 수 있는 서비스 프린시펄의 이름입니다. YARN에 제출할 때 이는 YARN 서비스가 실행되는 프린시펄이므로, 클라이언트 애플리케이션은 그 정보를 알아야 합니다.
다른 리소스 매니저의 경우 해당 갱신을 수행하는 서비스가 없으므로 renewer는 대부분 중요하지 않습니다.
어떤 토큰이 갱신되는가?
이것은 아마 가장 큰 주의점입니다.
이전 섹션에서 논의했듯이 DT는 서비스별이며 생성 및 갱신에 서비스별 라이브러리가 필요합니다. 즉, YARN이 애플리케이션 토큰을 갱신하려면 YARN에 필요합니다:
- 애플리케이션이 사용하는 모든 서비스의 클라이언트 라이브러리
- 애플리케이션이 사용하는 서비스에 연결하는 방법에 대한 정보
- 그 서비스에 연결할 권한
그러나 실제로는 대부분의 경우 YARN이 단일 HDFS 클러스터에 접근할 수 있으며 그것이 DT 갱신 기능의 전부입니다. YARN에 보내진 다른 토큰은 컨테이너에 배포되지만 갱신되지는 않습니다.
즉, 일부 다른 코드가 갱신을 처리하지 않는 한 그 토큰들은 최대 수명 훨씬 전에 만료됩니다.
또한 모든 클라이언트 라이브러리가 토큰 갱신을 구현하는 것은 아닙니다. Flink가 지원하는 서비스의 예를 들면 HBase 토큰의 renew() 메서드는 no-op입니다. 따라서 HBase 토큰을 "갱신"하는 유일한 방법은 새 토큰을 만드는 것입니다.
토큰이 영구히 만료되면 어떻게 되나?
마지막 주의점은 DT가 갱신과 무관하게 최대 수명이 있다는 것입니다. 그 마감 후에는 서비스에 연결하려면 새 토큰을 만들어야 합니다. 이는 위임 토큰 없이 서비스에 연결할 능력이 필요하다는 뜻이며, DT 외의 어떤 형태의 인증이 필요합니다.
이것은 감독 없이 실행되는 장기 실행 애플리케이션에서 특히 중요합니다. 며칠마다 누군가 터미널에 로그인해 비밀번호를 입력하지 않고도 계속 실행될 수 있어야 합니다.
위임 토큰 갱신 (Delegation Token Renewal)
위에서 설명한 문제 때문에 Flink는 다른 방식의 갱신을 구현합니다. 해결책은 절충입니다: 실제 토큰 갱신을 지원하지 않는 HBase 같은 서비스를 겨냥한 최저 공통 분모를 목표로 합니다. 다음 예시에서 우리는 모든 기반을 덮기 위해 Hadoop 생태계 구성 요소를 강조하며 논의를 진행하는데, AWS S3 같은 다른 것들에 비해 인증 관점에서 더 복잡하기 때문입니다.
Flink에서 DT "갱신"은 애플리케이션에 장기 자격증명(예: keytab)을 줌으로써 활성화됩니다. keytab은 Kerberos 비밀번호를 평문 텍스트 파일에 쓴 것과 동일하여 매우 민감합니다: 누구든 그 keytab 파일을 손에 넣으면 keytab에 저장된 자격증명이 KDC에서 유효한 한 그 사용자로 아무 서비스에나 인증할 수 있습니다.
keytab을 보유함으로써 Flink는 유효한 Kerberos TGT를 무기한 유지할 수 있습니다.
장기 자격증명이 있으면 Flink는 이전 것이 만료되면 구성된 서비스에 대해 새 DT를 생성합니다. 따라서 Flink는 이전 섹션에서 설명한 것처럼 토큰을 갱신하지 않습니다: 각 갱신 간격마다 새 토큰을 만들어 TM에 배포합니다.
이는 HBase 같은 서비스 지원 외에 또 다른 장점이 있습니다: 외부 갱신 서비스(예: YARN)에 대한 의존성을 제거합니다. 그렇게 하면 애플리케이션이 장기 자격증명을 가진 한, Kubernetes 같은 DT를 인식하지 못하는 리소스 매니저와도 Flink의 갱신 기능을 사용할 수 있습니다.
위임 토큰과 프록시 사용자 (Delegation Tokens and Proxy Users)
"프록시 사용자(Proxy users)"는 Hadoop 용어로의 가장(impersonation)입니다. 서비스가 허용하면 사용자 A가 서비스에 연결할 때 사용자 B를 가장할 수 있게 합니다.
Flink는 애플리케이션을 제출할 때 가장(impersonation)을 단순히 허용하지 않습니다. Spark는 가장을 지원하지만 토큰 갱신은 허용하지 않습니다. Flink는 주로 스트리밍 워크로드를 위해 설계되었으므로 이 기능을 추가해도 얻을 것이 많지 않습니다.
외부 생성 위임 토큰 (Externally Generated Delegation Tokens)
Flink는 Hadoop 자격증명을 관리하기 위해 UserGroupInformation(UGI) API를 사용합니다. 즉, Flink는 파일에서 DT를 자동으로 로드하는 기능을 상속합니다. Hadoop 클래스는 HADOOP_TOKEN_FILE_LOCATION 환경 변수가 정의되면 그 변수가 가리키는 토큰 캐시를 로드합니다.
이 기능은 사용자를 대신해 워크로드를 시작하는 서비스가 주로 사용합니다. 일반 사용자는 Flink 외부에서 그러한 토큰을 얻는 방법을 알아내야 하므로 이 기능을 거의 사용하지 않습니다.
참고로 Flink 자신도 DT를 얻을 수 있습니다. UGI가 특정 서비스에 대한 DT를 포함하고 Flink가 그 서비스에 대한 토큰을 얻도록 구성된 경우, 토큰이 먼저 로드된 다음 Flink의 로딩 메커니즘에 의해 덮어쓰여집니다.
위임 토큰 지원의 제한 사항 (Limitations of Delegation Token Support)
DT에 대해 말할 때 명심해야 할 특정 제한이 있습니다.
-
모든 DT가 실제로 갱신 기간을 노출하는 것은 아닙니다. 이는 일반적으로 클라이언트에 노출되지 않는 서비스 구성입니다. 이러한 이유로 특정 DT 제공자는 갱신 기간을 제공할 수 없어, 그 서비스의 구성을 해당 정보를 제공하는 다른 서비스와 어떻게든 동기화해야 합니다. DT가 필요해질 때 일반적으로 사용 가능한 HDFS 서비스가 이 정보를 제공하므로, 일반적으로 DT를 사용하는 모든 서비스가 갱신 기간에 대해 HDFS와 같은 구성을 사용하는 것이 좋습니다.
-
Flink는 사용자 애플리케이션 코드를 파싱하지 않으므로 어떤 위임 토큰이 필요할지 모릅니다. 이는 Flink가 사용 가능한 구성에 기반하여 가능한 한 많은 위임 토큰을 얻으려 시도한다는 뜻입니다. 즉, HBase 토큰 제공자가 활성화되어 있지만 앱이 실제로 HBase를 사용하지 않아도 DT가 여전히 생성됩니다. 그 경우 사용자는 언급한 제공자를 명시적으로 비활성화해야 합니다.
-
"주문형(on demand)"으로 DT를 만드는 것은 어렵습니다. Flink는 토큰을 사전에 획득/배포하고 주기적으로 재획득/재배포합니다. 그러나 장점은 적절한 구성이 있으면 Flink가 투명하게 처리하므로 사용자 코드가 DT에 대해 걱정할 필요가 없다는 것입니다.
-
동일한 서비스에 인증하는 외부 파일시스템 플러그인이 있습니다. 좋은 예는
s3-hadoop과s3-presto입니다. 둘 다 S3에 인증합니다. 서비스 이름은 다르지만 같은 서비스에 대한 토큰을 얻어 의도하지 않은 결과를 일으킬 수 있습니다. 같은 서비스에 대한 토큰을 얻으므로 같은 위치에 저장합니다. 같은 자격증명으로 함께 사용하면 토큰이 (단일 사용자에 속하는) 단일 스레드 방식으로 서로 덮어써지므로 문제가 없다는 것을 쉽게 알 수 있습니다. 그러나 플러그인이 서로 다른 사용자 자격증명으로 구성되면 데이터 처리에 사용될 토큰이 어느 사용자에게나 속할 수 있어 비결정적입니다.