Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

외부 Iceberg 클라이언트 인증

Foundry 안에서 Iceberg를 사용하는 경우에는 이 외부 클라이언트 인증 구성이 필요하지 않습니다. 외부 엔진은 Iceberg REST Catalog 인증 규약에 따라 사용자 API 토큰 또는 OAuth2 client credentials로 Foundry 카탈로그에 연결할 수 있습니다.

클라이언트에 원래 S3 키나 ADLS 인증 정보를 직접 설정하지 않습니다. 카탈로그가 테이블 권한을 확인한 뒤 범위를 제한한 단기 저장소 인증 정보를 제공하도록 구성합니다.

인증 성공과 테이블 형식 호환성은 별개입니다. 카탈로그 구현별 생성 요청 지원, 클라이언트의 v2/v3 읽기 및 쓰기 지원, CSE 지원을 각각 확인합니다.

사용자 API 토큰

API 토큰은 사용자에게 연결된 Bearer 토큰입니다. 개발과 초기 연결 확인에는 사용할 수 있지만 장기간 유효하고 사용자의 권한으로 동작하므로 주의해서 보관합니다. 운영에는 OAuth2가 권장됩니다.

속성필수 여부값
uri필수Foundry HTTPS 기본 URL 뒤의 /iceberg
token필수실제 Bearer 토큰 원문

PyIceberg 설정 파일을 사용하면 ~/.pyiceberg.yaml의 catalog 아래에 카탈로그 이름, 예를 들어 foundry를 두고 그 안에 uri와 token을 설정합니다. 이 파일에는 인증 정보가 들어가므로 접근을 제한하고 코드 저장소에 넣지 않습니다.

파일에 토큰을 저장하지 않고 다음 함수에 실제 Foundry 기본 URL을 전달할 수도 있습니다. getpass 입력은 화면에 표시되지 않습니다.

from getpass import getpass
from pyiceberg.catalog import load_rest


def create_bearer_catalog(foundry_url: str):
    token = getpass("Foundry 사용자 토큰: ")
    return load_rest("foundry", {
        "uri": foundry_url.rstrip("/") + "/iceberg",
        "token": token,
    })

카탈로그 객체는 연결에 필요한 인증 정보를 보관하므로 객체나 구성 전체를 로그에 출력하지 않습니다.

OAuth2 client credentials

Client credentials grant를 사용하는 third-party application을 등록하고 Client ID와 Secret을 준비합니다. 앱의 서비스 사용자에 필요한 Compass 폴더 및 Iceberg 테이블 권한을 부여해야 합니다. 인증 정보가 있다고 리소스 접근 권한이 자동으로 생기지는 않습니다.

속성필수 여부값
uri필수Foundry 기본 URL 뒤의 /iceberg
oauth2-server-uri필수Foundry 기본 URL 뒤의 /iceberg/v1/oauth/tokens
credential필수실제 Client ID와 Secret을 client_id:client_secret 형식으로 연결한 문자열
scope선택공백으로 구분한 api:iceberg-read, api:iceberg-write 등의 범위

Iceberg 클라이언트는 토큰 교환과 이후 갱신을 처리합니다. 이 구성은 Iceberg 전용 인증 서버를 사용하며 일반 Foundry OAuth 클라이언트의 Multipass 엔드포인트로 대체하지 않습니다.

scope를 생략하면 서비스 사용자의 권한이 기본 범위가 됩니다. 읽기만 필요하면 api:iceberg-read만 지정하고, 쓰기가 필요할 때 api:iceberg-write를 추가합니다. scope는 기존 리소스 권한을 제한할 수 있지만 없는 권한을 새로 부여하지 않습니다.

from getpass import getpass
from pyiceberg.catalog import load_rest


def create_oauth_catalog(foundry_url: str, client_id: str, scope=None):
    base_url = foundry_url.rstrip("/")
    client_secret = getpass("OAuth2 Client Secret: ")
    configuration = {
        "uri": base_url + "/iceberg",
        "oauth2-server-uri": base_url + "/iceberg/v1/oauth/tokens",
        "credential": f"{client_id}:{client_secret}",
    }
    if scope is not None:
        configuration["scope"] = scope
    return load_rest(name="foundry", conf=configuration)

설정 파일을 사용할 때도 catalog의 해당 카탈로그 이름 아래에 같은 네 속성을 둡니다. 비밀 정보는 공유 노트북이나 버전 관리 파일에 직접 적지 않습니다.

저장소 접근 위임

카탈로그의 접근 위임은 credential vending 또는 지원되는 저장소의 remote signing으로 이루어집니다. 다음은 ABFS와 S3의 지원 방식입니다.

저장소Credential vending인증 정보 갱신Remote signing
ABFSSAS 토큰으로 지원미지원미지원
S3STS 토큰으로 지원지원지원

Credential vending은 권장 방식입니다. 흐름은 다음과 같습니다.

  1. 클라이언트가 테이블 메타데이터 등 카탈로그 리소스에 접근을 요청합니다.
  2. 카탈로그가 호출자를 인증하고 해당 테이블 및 작업의 권한을 확인합니다.
  3. 허용되면 특정 파일과 작업 범위에 한정된 단기 저장소 인증 정보를 발급합니다.
  4. 클라이언트가 그 인증 정보로 저장소에 직접 접근합니다. 유효 기간이 끝나면 해당 인증 정보로 접근할 수 없습니다.

저장소 연결은 별도의 네트워크 접근도 필요합니다.

인증 문제 구분

먼저 개발용 사용자 토큰으로 카탈로그에 연결하여 테이블 메타데이터를 읽을 수 있는지 확인합니다. 이후 OAuth2 클라이언트로 같은 경로를 확인하면 네트워크 및 사용자 접근 문제와 앱 설정 문제를 구분할 수 있습니다.

다음 함수는 실제 초기화한 카탈로그에서 이름 공간과 대상 테이블의 스냅샷 메타데이터를 반환합니다. table_identifier에는 실제 테이블 RID 또는 이름 공간을 포함한 식별자를 전달합니다.

def inspect_catalog_access(catalog, table_identifier):
    namespaces = catalog.list_namespaces()
    table = catalog.load_table(table_identifier)
    return {"namespaces": namespaces, "snapshots": table.snapshots()}

사용자 토큰으로는 되지만 OAuth2로 실패하면 앱의 client credentials 설정, scope와 서비스 사용자의 폴더 및 테이블 권한을 확인합니다. 이름 공간 목록과 특정 테이블 접근 권한도 서로 구분합니다.

스냅샷 목록 조회는 카탈로그 및 테이블 메타데이터 접근을 확인하는 것이며 저장소 파일을 읽었다는 증거는 아닙니다. 실제 파일 접근은 카탈로그와 버킷 진단을 사용하고, 전체 테이블 읽기는 적절한 크기의 시험 테이블에서 다음처럼 수행할 수 있습니다.

def read_table_as_pandas(catalog, table_identifier):
    return catalog.load_table(table_identifier).scan().to_pandas()

이 함수는 실제 테이블 데이터를 로컬 메모리로 가져옵니다. 생성과 쓰기까지의 흐름은 로컬 Jupyter 예제에 있습니다.