← 학습 카테고리

Learn

Airflow

151개 모듈 · 현재 37번째

Airflow 모듈 37/151 airflow-learn-37

Auth Manager 개념과 커스텀 구현

Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation core-concepts/auth-manager/index.rst 전체

이 모듈을 다 읽으면

  • Auth Manager가 담당하는 인증/인가 책임 범위를 설명할 수 있다
  • is_authorized_* 계열 메서드에서 method 파라미터가 HTTP 동사에 대응하는 규칙을 설명할 수 있다
  • team_name을 받는 is_authorized_view의 하위호환 처리 방식을 설명할 수 있다
  • JWT 토큰이 auth manager와 Airflow UI 사이에서 어떻게 전달되는지 설명할 수 있다

Auth manager는 사용자 인증과 인가를 처리하는 Airflow 컴포넌트로, 공통 인터페이스를 구현하는 한 교체 가능하다(pluggable). 이 모듈은 Auth manager의 위치와 교체 비용, 인증·인가 관련 메서드, 3.4.0에 추가된 team_name 인자의 하위호환 처리, 그리고 JWT 토큰 발급·전달·갱신 메커니즘을 다룬다.

Auth Manager란 — 하나만 설정 가능한 pluggable 컴포넌트

Auth(인증/인가) manager는 사용자 인증과 사용자 인가를 다루는 Airflow 컴포넌트다. 공통 API를 가지며 pluggable하다 — 설치 요구사항에 따라 auth manager를 교체할 수 있다. Airflow는 한 번에 하나의 auth manager만 설정할 수 있으며, [core] 섹션의 auth_manager 옵션으로 지정한다. 현재 설정을 확인하려면 `airflow config get-value core auth_manager`를 쓴다.

Auth manager가 pluggable한 이유는, 매우 다양한 사용자 규모를 가진 환경들을 하나의 사용자 관리 방식으로 커버할 수 없기 때문이다. 어떤 Airflow 환경은 사용자가 한 명뿐이고, 어떤 환경은 수천 명이 쓴다 — 사용자가 거의 없는 환경이 수천 명 규모 환경과 같은 사용자 관리를 필요로 하지는 않는다. 그래서 사용자 관리(인증 + 인가) 전체를 auth manager라는 하나의 컴포넌트로 패키징해, 필요에 맞는 것을 손쉽게 갈아 끼울 수 있게 했다. auth manager를 바꾸는 것은 무거운 작업으로 간주해야 한다 — 사용자 경험에 영향을 준다. 로그인/로그아웃 경험이 크게 바뀌어 사전 공지 없이는 사용자를 혼란스럽게 할 수 있고, 기존 사용자와 권한을 이전 auth manager에서 새 것으로 옮겨야 한다.

핵심 포인트

  • Airflow는 한 번에 하나의 auth manager만 설정할 수 있으며 [core] auth_manager로 지정한다
  • auth manager를 교체하는 것은 무거운 작업이다 — 로그인 경험이 바뀌고 기존 사용자·권한을 새 매니저로 옮겨야 한다
  • 다양한 규모(1인 환경부터 수천 사용자)를 하나의 사용자 관리 방식으로 커버할 수 없다는 것이 pluggable 설계의 이유다

인증 관련 메서드와 사용자 표현

BaseAuthManager는 인증된 사용자 타입을 나타내는 타입 파라미터 T로 매개변수화된다. auth manager 구현체(BaseAuthManager의 서브클래스)는 자신만의 구체적인 사용자 타입을 지정해야 하며, 이 사용자 타입은 BaseUser의 서브클래스여야 한다.

인증 관련 메서드로는: get_url_login(사용자가 로그인을 위해 리다이렉트되는 URL을 반환), get_url_logout(로그아웃 시 리다이렉트되는 URL을 반환하는 선택적 메서드로, 세션 같은 리소스를 무효화하기 위한 리다이렉션에 보통 쓰인다), serialize_user(사용자 인스턴스를 dict로 직렬화하며, 이 dict가 곧 JWT 토큰의 실제 내용이 된다 — 사용자를 식별하고 인가 요청을 하는 데 필요한 모든 정보를 담아야 한다), deserialize_user(그 dict, 즉 JWT 토큰의 payload로부터 사용자 인스턴스를 만든다)가 있다.

핵심 포인트

  • serialize_user가 만든 dict가 곧 JWT 토큰의 실제 payload이며, deserialize_user는 그 payload로부터 사용자 인스턴스를 복원한다
  • 각 auth manager는 자신만의 사용자 타입을 정의하며, 그 타입은 BaseUser의 서브클래스여야 한다

인가 관련 메서드 — method 파라미터와 리소스별 is_authorized_*

대부분의 인가 메서드는 같은 형태의 파라미터를 쓴다. method는 특정 리소스에 어떤 종류의 액션이 수행되는지를 HTTP 메서드 이름으로 나타낸다 — GET은 리소스를 읽을 수 있는지, POST는 만들 수 있는지, PUT은 수정할 수 있는지, DELETE는 삭제할 수 있는지를 묻는다. details는 접근하는 리소스에 대한 선택적 상세 정보이고, user는 리소스에 접근하려는 사용자다.

인가 메서드에는: is_authorized_configuration(Airflow 설정 접근 가능 여부, 설정 섹션 등 상세를 줄 수 있음), is_authorized_connection(Airflow 커넥션 접근 가능 여부), is_authorized_dag(Dag 접근 가능 여부이며, 태스크 인스턴스나 Dag run처럼 Dag와 관련된 모든 엔터티에 대해서도 이 메서드가 호출된다 — access_entity 파라미터로 이 정보를 전달한다. 예: `is_authorized_dag(method="GET", access_entity=DagAccessEntity.Run, details=DagDetails(id="dag-1"))`는 사용자가 Dag 'dag-1'의 Dag run들을 읽을 권한이 있는지 묻는다), is_authorized_asset(자산 접근 가능 여부), is_authorized_asset_alias(자산 별칭 접근 가능 여부), is_authorized_pool(풀 접근 가능 여부), is_authorized_variable(변수 접근 가능 여부), is_authorized_view(access_view로 지정된 특정 뷰 — 예: AccessView.CLUSTER_ACTIVITY — 접근 가능 여부이며, 선택적 team_name으로 팀 스코프 체크도 가능), is_authorized_custom_view(Airflow에 정의되지 않은, auth manager 자체나 사용자 정의 플러그인이 제공하는 뷰에 대한 접근 가능 여부), filter_authorized_menu_items(UI 메뉴 항목 목록 중 사용자가 접근 가능한 항목만 걸러 반환)가 있다.

method 파라미터는 이 메서드들 중 일부 하위 집합에만 의미가 있다는 점에 유의해야 한다 — 예를 들어 configuration 리소스는 정의상 읽기 전용이므로 is_authorized_configuration에서는 GET만 의미가 있다.

Dag 하위 컴포넌트 인가: Dag와 그 구성 리소스(Dag run, 태스크, 태스크 인스턴스)의 계층 구조를 고려해, auth manager의 is_authorized_dag 메서드는 이들의 인가 로직도 함께 처리한다. access_entity 파라미터는 사용자가 시도하는 것이 Dag의 어느 하위 컴포넌트인지(있다면) 나타낸다. access_entity가 None이면 사용자는 Dag 자체와 직접 상호작용하려는 것이다. None이 아니면 Dag의 하위 컴포넌트에 접근하려는 것인데, 이때는 method가 그 하위 엔터티에는 유효하지만 Dag 자체에는 유효하지 않은 액션일 수 있다는 점이 중요하다 — 예를 들어 POST는 Dag run에는 유효하지만 Dag 자체에는 유효하지 않다. 이런 요청을 모델링하는 한 가지 방법은, 사용자가 다음 두 가지를 모두 만족할 때만 인가하는 것이다: 해당 Dag에 대한 PUT("수정") 권한이 있을 것, Dag run에 대한 POST("생성") 권한이 있을 것.

핵심 포인트

  • method 파라미터는 HTTP 동사 관례를 따른다: GET=조회, POST=생성, PUT=수정, DELETE=삭제
  • is_authorized_dag는 access_entity 파라미터로 Dag 자체뿐 아니라 Dag run·태스크·TI 같은 하위 컴포넌트 인가도 함께 처리한다
  • POST는 Dag run에는 유효한 동작이지만 Dag 자체에는 유효하지 않을 수 있다 — 이런 경우 Dag PUT 권한과 Dag run POST 권한을 모두 요구하는 식으로 모델링한다

team_name을 받는 is_authorized_view (3.4.0+)의 하위호환

3.4.0부터 is_authorized_view는 선택적 team_name 인자를 받는다. 멀티팀 배포에서는 읽기 전용 뷰에 대한 접근을 특정 팀의 사용자로 제한할 수 있는데, is_authorized_view가 이 목적으로 team_name을 받는다. 멀티팀 격리를 구현한 auth manager는 이를 존중해 team_name에 속한 사용자만 인가하고, 멀티팀을 지원하지 않는 auth manager는 이 인자를 받되 무시한다 — 이는 이 인자가 생기기 전과 같은 동작(모든 팀에 걸쳐 뷰를 인가)이다.

코어는 is_authorized_view를 team_name과 함께 직접 호출하지 않는다. 대신 BaseAuthManager.authorize_view를 거치는데, 이 메서드가 먼저 auth manager의 is_authorized_view가 team_name을 받는지 확인한다. 이 덕분에 이 인자가 생기기 전의 auth manager도 계속 동작한다 — 여전히 옛 시그니처((access_view, user))를 가진 커스텀·서드파티 auth manager는 team_name 없이 호출되며(에러를 내지 않는다), 대신 특정 팀으로 제한되어야 할 일부 뷰가 auth manager가 업그레이드될 때까지 모든 팀에 걸쳐 인가된다는 것을 경고하는 RemovedInAirflow4Warning이 발생한다.

auth manager를 팀 인식으로 만들려면 오버라이드에 team_name을 추가하면 된다(멀티팀을 지원하지 않는 매니저는 받되 무시해도 된다):

def is_authorized_view(
    self, *, access_view: AccessView, user: MyUser, team_name: str | None = None
) -> bool: ...

이 옛 시그니처에 대한 폴백은 Airflow 4에서 제거되어, team_name이 필수가 된다.

핵심 포인트

  • is_authorized_view의 team_name 인자는 3.4.0에 추가되었고, BaseAuthManager.authorize_view가 시그니처를 검사해 구버전 auth manager는 team_name 없이 호출한다
  • team_name을 받지 않는 구버전 auth manager는 에러 없이 동작하지만 RemovedInAirflow4Warning이 발생하고, 팀 제한 뷰가 모든 팀에 노출된다는 뜻이다
  • Airflow 4부터는 이 하위호환 폴백이 제거되어 team_name이 필수가 된다

JWT 토큰 관리, 확장 포인트, 유의사항

auth manager는 Airflow public API와 상호작용하는 데 필요한 JWT 토큰을 만들 책임이 있다. 이를 위해 auth manager는 이 토큰을 만드는 엔드포인트를 반드시 제공해야 하며, 보통 POST /auth/token에서 제공된다. auth manager는 또한 JWT 토큰을 Airflow UI에 전달할 책임도 있다 — auth manager와 UI 사이의 토큰 교환은 쿠키를 이용하는데, auth manager는 UI로 리다이렉트하기 전에 _token이라는 이름의 쿠키에 JWT 토큰을 저장해야 한다. UI는 이 쿠키를 읽어 저장한 뒤 쿠키를 삭제한다. httponly 쿠키 파라미터는 반드시 True로 설정해야 한다 — UI는 토큰 자체를 관리하지 않는다.

토큰 갱신은 선택 기능으로 auth manager 구현에 따라 지원 여부가 다르다. 토큰이 만료되면 JWTRefreshMiddleware가 auth manager의 refresh_user 메서드를 호출해 새 토큰을 얻는다. 이 메서드는 만료된 토큰을 받아 새로운 유효한 토큰을 반환해야 하며, 사용자 정보는 만료된 토큰에서 추출해 새 토큰을 만드는 데 쓰인다.

최적화를 위해 오버라이드하는 것이 권장되는 선택적 메서드들도 있다: batch_is_authorized_connection/dag/pool/variable(각각의 단건 메서드의 배치 버전으로, 오버라이드하지 않으면 항목마다 단건 메서드를 반복 호출한다), filter_authorized_connections/dag_ids/pools/variables(주어진 ID 목록 중 사용자가 접근 가능한 것만 걸러 반환하며, 마찬가지로 오버라이드하지 않으면 항목마다 단건 메서드를 호출한다), is_authorized_hitl_task(Human-in-the-loop 태스크 승인/거부 권한 — 오버라이드하지 않으면 사용자 ID가 배정된 사용자 목록에 있는지로 판단한다).

auth manager는 get_fastapi_app 메서드를 구현해 API 서버 애플리케이션을 확장할 수 있다 — 이를 통해 auth manager가 다루는 사용자·그룹·역할 같은 리소스를 관리하는 추가 공개 API 엔드포인트를 제공할 수 있으며, get_fastapi_app이 정의한 엔드포인트는 /auth 아래에 마운트된다. 그 밖의 선택적 메서드로는 init(Airflow 초기화 시 실행되며, auth manager에 필요한 리소스 생성·API 호출 등을 여기서 수행), get_extra_menu_items(UI 메뉴에 추가 링크 제공), get_db_manager(auth manager가 필요로 하는 데이터베이스 매니저의 클래스 경로를 반환하면 Airflow가 자동으로 로드)가 있다.

유의사항: auth manager는 폐지 예정인 airflow.security.permissions 모듈을 참조하면 안 되며 대신 airflow.api_fastapi.auth.managers.models.resource_details의 정의를 써야 한다. Dag 인스턴스의 access_control 속성은 FAB auth manager와만 호환되므로, 커스텀 auth manager 구현은 Dag 레벨 접근 제어(예: Dag 태그·번들 기반)를 위해 get_authorized_dag_ids를 활용해야 한다. 각 공개 is_authorized_* 메서드가 호출하는, 표준화된 인가 메커니즘 역할을 하는 private한 일반화된 _is_authorized 메서드를 두는 것이 유용할 수 있다(구체적인 예시는 SimpleAuthManager._is_authorized_method 참고).

핵심 포인트

  • JWT 토큰은 auth manager가 발급하며 보통 POST /auth/token 엔드포인트로 제공하고, _token 쿠키(httponly=True)로 UI에 전달한 뒤 UI가 읽고 삭제한다
  • 토큰 갱신은 선택 기능으로, 만료 시 JWTRefreshMiddleware가 auth manager의 refresh_user를 호출한다
  • batch_is_authorized_*와 filter_authorized_* 메서드를 오버라이드하지 않으면 항목마다 단건 메서드를 반복 호출하므로, 성능이 중요하면 배치 버전을 구현하는 것이 좋다
  • Dag.access_control 속성은 FAB auth manager 전용이므로, 커스텀 auth manager는 get_authorized_dag_ids로 Dag 레벨 접근 제어를 구현해야 한다