Airflow 보안 모델
Airflow 보안 모델 (Airflow Security Model)
이 문서는 Airflow 사용자 관점에서 Airflow의 보안 모델을 설명해요. 사용자가 보안 모델을 이해하고 Airflow를 배포·관리하는 방법에 대해 정보에 입각한 결정을 내리도록 돕는 것이 목적이에요.
출처: 문서
본문
보안 취약점을 보고하는 방법과 보안 보고서가 Airflow 보안 팀에서 어떻게 처리되는지 알고 싶다면 Airflow의 보안 정책을 참고하세요.
Airflow 보안 모델 - 사용자 유형
Airflow 보안 모델은 접근과 기능이 다른 다양한 유형의 사용자들이 관여해요:
작은 설치에서 Airflow와 관련된 모든 작업을 단일 사용자가 수행할 수 있지만, 큰 설치에서는 분리해야 할 서로 다른 책임, 역할, 기능이 있다는 것이 분명해요.
이 때문에 Airflow는 다음과 같은 사용자 유형을 갖고 있어요:
- Deployment Managers - Airflow 설치, 보안, 구성을 전반적으로 담당
- 인증된 UI 사용자 (Authenticated UI users) - Airflow UI와 API에 접근해 상호작용할 수 있는 사용자
- Dag 작성자 (Dag authors) - Dags를 만들고 Airflow에 제출하는 책임
사용자 유형이 Airflow 아키텍처에 어떻게 영향을 주는지는 아키텍처 개요에서 더 볼 수 있어요. 덜 복잡하고 더 복잡한 배포의 다이어그램을 포함해요.
Deployment Managers
가장 높은 수준의 접근과 통제권을 가져요. Airflow를 설치·구성하고, 기술과 권한에 대한 결정을 내려요. 잠재적으로 전체 설치를 삭제할 수 있고 모든 자격 증명에 접근할 수 있어요. Deployment Managers는 감사, 백업, 정보의 사본을 Airflow 외부에 두기로 결정할 수도 있는데, 이것은 Airflow의 보안 모델이 다루지 않아요.
Dag 작성자 (Dag authors)
Dag 파일을 만들고, 수정하고, 삭제할 수 있어요. Dag 파일의 코드는 워커, Dag File Processor, Triggerer에서 실행돼요. 따라서 Dag 작성자는 워커, Dag File Processor, Triggerer에서 실행되는 코드를 만들고 변경할 수 있으며, 잠재적으로 Dag 코드가 외부 시스템에 접근하는 데 사용하는 자격 증명에 접근할 수 있어요.
Airflow 3에서 데이터베이스 격리 수준은 컴포넌트에 따라 달라져요:
- 워커 (Workers): 워커의 Task 코드는 Execution API를 통해서만 API server와 통신해요. 워커는 데이터베이스 자격 증명을 받지 않으며 메타데이터 데이터베이스에 직접 접근할 수 없어요.
- Dag File Processor와 Triggerer: Airflow는 Dag 작성자 코드가 우발적으로 직접 데이터베이스에 접근하는 것을 방지하는 소프트웨어 가드를 구현해요. 하지만 Dag 파싱과 trigger 실행 프로세스가 (데이터베이스 자격 증명을 가진) 부모 프로세스와 같은 Unix 사용자로 실행되므로, 의도적으로 악의적인 Dag 작성자는 부모 프로세스에서 자격 증명을 검색해 직접 데이터베이스 접근을 얻을 수 있어요. 구체적인 메커니즘과 배포 강화 조치는 JWT 인증과 워크로드 격리를 참고하세요.
인증된 UI 사용자 (Authenticated UI users)
UI와 API에 접근할 수 있어요. 인증된 UI 사용자가 가질 수 있는 기능에 대한 자세한 내용은 아래를 참고하세요.
비인증 UI 사용자 (Non-authenticated UI users)
Airflow는 기본적으로 비인증 사용자를 지원하지 않아요. 허용한다면 잠재적 취약점을 Deployment Manager가 평가하고 해결해야 해요. 하지만 예외가 있어요. 상태 확인 업데이트를 담당하는 /api/v2/monitor/health 엔드포인트는 공개적으로 접근 가능해야 해요. 다른 시스템이 그 정보를 가져오려 하기 때문이에요. 또 다른 예외는 /login 엔드포인트인데, 사용자가 이를 사용하려면 인증되지 않은 상태일 것으로 기대되기 때문이에요.
인증된 UI 사용자의 기능
인증된 UI 사용자의 기능은 Deployment Manager나 Admin 사용자가 구성한 역할과 그 역할이 가진 권한에 따라 달라질 수 있어요. 역할의 권한은 예를 들어 단일 Dag만큼 좁게, 또는 Admin만큼 넓게 범위를 지정할 수 있어요. 인증된 사용자가 가질 수 있는 기능을 개념화하는 데 도움이 되는 네 가지 일반 범주가 아래 있어요:
Admin 사용자
다른 사용자에게 권한을 관리하고 부여하며, 모든 UI 기능에 대한 전체 접근 권한을 가져요. connection을 구성해 워커에서 코드를 실행할 수 있으므로 이러한 권한을 남용하지 않도록 신뢰받아야 해요. 민감한 자격 증명에 접근하고 수정할 수 있어요. 기본적으로 시스템 수준 구성에는 접근할 수 없어요. connection 구성을 통해 접근 가능한 민감한 정보를 오용하지 않도록 신뢰받아야 해요. 또한 API Server Denial of Service 상황을 만들 수 있는 능력이 있으며 이 기능을 오용하지 않도록 신뢰받아야 해요.
기본적으로 감사 로그에 접근할 수 있는 사용자는 Admin 사용자뿐이에요.
Operations 사용자
operator와 admin의 주된 차이는 다른 사용자에게 권한을 관리하고 부여하는 능력과 감사 로그 접근이에요 — 오직 admin만 이것을 할 수 있어요. 그 외에는 admin과 같은 접근 권한이 있다고 가정하세요.
Connection 구성 사용자
Connection을 구성하고 Dag 실행 중 워커에서 코드를 실행할 수 있어요. 이러한 권한의 오용을 방지하려면 신뢰가 필요해요. connection에 저장된 민감한 자격 증명에 대한 전체 쓰기 전용 접근 권한이 있고 수정할 수 있지만, 볼 수는 없어요. connection 구성을 통해 민감한 정보를 쓰는 접근이 남용되지 않도록 신뢰받아야 해요. 또한 일부 providers(커뮤니티 배포 또는 커스텀)에서 Dags를 실행하면 임의 원격 코드 실행(Remote Code Execution)으로 이어질 수 있는 connection 옵션을 안전하지 않게 지정하거나, API Server Denial of Service 상황을 만들 수 있는 connection을 잘못 구성할 능력도 있어요.
이런 기능을 오용하지 않도록 사용자는 높은 신뢰를 받아야 해요.
Note
Airflow 3 이전에는 Connection 구성 사용자 역할도 민감한 정보를 보는데 접근할 수 있었는데, Airflow 3에서 connection 구성 사용자의 자격 증명이 우발적으로 유출되는 것을 보안상 개선하기 위해 변경됐어요. 이전 — Airflow 2에서 — Connection 구성 사용자는 의도적으로 민감한 정보를 볼 수 있는 접근 권한이 있었고, 브라우저의 Inspect 기능으로 드러내거나 구성 extras에 저장된 민감한 자격 증명의 경우 그냥 평문으로 보였어요. Airflow 3 및 이후 버전은 API 수준에서 이러한 민감한 자격 증명을 마스킹하고 평문으로 반환하지 않아요.
민감한 정보에 대해 (About Sensitive information)
민감한 정보는 connection 세부 정보, variables, 구성을 포함해요. Airflow 3.0 이후 버전에서는 민감한 정보가 API, UI, airflowctl을 통해 사용자에게 노출되지 않아요. 하지만 task-sdk는 여전히 민감한 정보에 대한 접근을 제공해요(예: SDK API Client를 사용해 task별 JWT 토큰으로 Variables 가져오기). 로컬 CLI는 --show_values를 사용할 때를 제외하고 키만 반환해요. 민감한 정보는 로그, UI, API 출력에서 마스킹됐어요. Dag 작성자가 다른 방식(예: 환경 변수를 통해)으로 민감한 정보를 노출하는 경우, 그 값들은 마스킹되지 않아요.
감사 로그 사용자 (Audit log users)
전체 Airflow 설치에 대한 감사 이벤트를 볼 수 있어요.
일반 사용자 (Regular users)
UI와 API를 보고 상호작용할 수 있어요. Dags, task instances, Dag runs을 보고 편집할 수 있고 task 로그를 볼 수 있어요.
Viewer 사용자
Dags, task 로그 및 기타 관련 세부 정보에 관련된 정보를 읽기 전용 방식으로 볼 수 있어요. 이 역할은 Dags를 트리거하거나 수정할 수 없는 읽기 전용 접근이 필요한 사용자에게 적합해요.
Viewers는 또한 감사 로그에 접근할 권한이 없어요.
인증된 UI 사용자의 기능에 대한 자세한 정보는 FAB auth manager와 접근 제어를 참고하세요.
Dag 작성자의 기능
Dag 작성자는 Dag bundle에 배치된 Python 파일을 통해 — 다양한 상황에서 실행될 — 코드를 만들거나 편집할 수 있어요. 실행할 코드는 Airflow가 검증·확인·샌드박스 처리하지 않아요(불가능에 가까울 만큼 어려움). 그래서 실질적으로 Dag 작성자는 워커(Celery Executor의 경우 Celery Workers의 일부, Local Executor의 경우 스케줄러가 실행하는 로컬 프로세스, Kubernetes Executor의 경우 Task Kubernetes POD), Dag Processor, Triggerer에서 임의 코드를 실행할 수 있어요.
Dag 작성자는 자신이 작성해 Airflow에 제출하는 코드에 책임이 있으며, 구현하는 것이 안전한 코드이고 Airflow 설치에 어떤 해도 끼치지 않으며 보안 취약점의 길을 열지 않을 것을 검증하도록 신뢰받아야 해요. Dag 작성자는 Python 코드를 작성하므로, Airflow에 저장된 민감한 정보에 접근하거나 그것을 외부로 보내는 코드를 쉽게 작성할 수 있어요 — 하지만 새로운 보안 취약점을 열 수도 있어요. 좋은 예는 소독(sanitize)되지 않은 UI 사용자 입력(파라미터, variables, connection 구성 등)을 Operators와 Hooks의 코드나 제3자 라이브러리에 올바르게 소독하지 않고 전달하는 코드를 작성하는 것이에요. 이것은 Remote Code Execution, Denial of Service 취약점 등의 창을 열 수 있어요. Dag 작성자는 그런 코드를 작성하지 않고, 작성한 코드가 안전하며 새 보안 취약점을 열지 않는다고 검증하도록 신뢰받아야 해요.
Dag 작성자의 Dags 부분집합 접근 제한
Airflow는 아직 task 실행에 관해 서로 다른 사용자 그룹 사이의 완전한 task 수준 격리를 제공하지 않아요. Airflow 3.0 이상에서 워커 task 코드는 메타데이터 데이터베이스에 직접 접근할 수 없지만(Execution API를 통해 통신), Dag File Processor와 Triggerer에서 실행되는 Dag 작성자 코드는 잠재적으로 여전히 직접 데이터베이스 접근을 가져요. 실행 컨텍스트와 관계없이 Dag 작성자는 Airflow 설치의 모든 Dags에 접근할 수 있고 그 중 어느 Dag이든 수정할 수 있어요 — task 코드가 어떤 Dag을 위해 실행되는지와 무관하게요. 즉 Dag 작성자는 어떤 Dag의 어떤 task instance든 상태를 수정할 수 있으며, 그 접근을 제한할 더 세분화된 접근 제어가 없어요.
이것은 Dag 작성자의 코드가 도달할 수 있는 모든 인터페이스에 적용돼요 — 워커가 Task SDK로 사용하는 Task Execution API를 포함해서요. 실행 중인 task에 발급된 Execution JWT는 Dag별 권한을 담지 않아요: 유효한 토큰을 가진 ***는 설치 내 어느 Dag에 대해서든 상태 변형 Execution API 엔드포인트(Dag runs 트리거, Dag runs clear, variables·connections·XComs 읽기·쓰기 등)를 호출할 수 있어요. ti:self 토큰 스코프는 교차 task instance 상태 변형만 제한해요. Dag별 접근 제어가 아니에요.
Airflow에는 실험적 multi-team 기능([core] multi_team)이 있어 팀 간 UI 수준 및 REST API 수준 RBAC 격리를 제공해요. multi-team 모드에서 Task Execution API를 통해 도달할 수 있는 팀 스코프 리소스도 팀별로 격리돼요: task는 자신의 팀에 속한 Variables와 Connections에만 접근할 수 있고(전역 값으로 폴백), 자신의 팀 Dags의 XComs에만 접근할 수 있어요(읽기는 전역 Dags에도 도달할 수 있지만 쓰기와 삭제는 불가).
하지만 이 기능은 아직 task 수준 격리를 보장하지 않아요. task 실행 수준에서 서로 다른 팀의 워크로드는 여전히 같은 Execution API, 서명 키, connections, variables를 공유해요. 한 팀의 task는 다른 팀의 task와 같은 공유 리소스에 접근할 수 있어요. multi-team 기능은 진행 중인 작업이에요 — task 수준 격리와 Execution API의 팀 경계 강제는 향후 Airflow 버전에서 개선될 거예요. 그때까지 모든 Dag 작성자가 모든 Dags와 공유 리소스에 접근하고, 팀 할당과 관계없이 그 상태를 수정할 수 있다고 가정해야 해요.
Dag별 읽기 접근과 소스 코드 검색
dag-source 검색 엔드포인트(GET /api/v2/dagSources/{dag_id})는 현재 Dag-to-file 매핑에 대한 Dag별 읽기 스코프를 존중해요: 요청된 Dag을 뒷받침하는 파일이 호출자가 읽기 권한이 없는 다른 Dags도 정의한다면, 엔드포인트는 소스 대신 검열된 자리 표시자(redacted placeholder)를 반환해요.
엔드포인트는 선택적 version_number 쿼리 파라미터로 과거 소스를 검색하는 것도 지원해요. 역사적 버전의 경우 Dag별 스코프는 요청된 버전이 저장되었을 때의 파일 내용과 다를 수 있는 현재 파일 구성원을 사용해 강제돼요. 결과적으로 이전 버전을 요청하면 파일에서 이후 제거된 Dag이 포함된 소스가 반환될 수 있어요, 호출자가 현재 그 제거된 Dag에 대한 읽기 접근이 없더라도요. 반대로, 이후 추가된 같은 파일의 Dag이 호출자의 읽기 가능 세트에 없으면 요청한 역사적 소스가 그 추가보다 앞선 것이더라도 검열된 자리 표시자가 반환될 수 있어요.
소스 격리를 위해 Dag별 읽기 스코프에 의존하는 배포는 소스 파일당 하나의 Dag을 유지하거나, DagAccessEntity.CODE를 어떤 소스 파일에도 함께 존재했던 모든 Dag을 읽도록 신뢰되는 역할로 제한해야 해요.
Dag 작성자 제출 코드의 보안 맥락
Airflow가 선택한 모델의 몇 가지 결과가 있는데, 배포 관리자가 Dag 작성자의 기능이 Airflow의 다른 보안 맥락에서 실행되는 코드에 어떻게 매핑되는지 알아야 해요:
Local executor
Local Executor의 경우 Dag 작성자는 스케줄러가 실행 중인 머신에서 임의 코드를 실행할 수 있어요. 이는 스케줄러 프로세스 자체에 영향을 줄 수 있고, 클러스터 전체 정책 수정과 Airflow 구성 변경을 포함해 전체 Airflow 설치에 영향을 줄 수 있다는 뜻이에요. Local Executor로 Airflow를 실행한다면 Deployment Manager는 Dag 작성자가 이 기능을 남용하지 않을 것이라고 신뢰해야 해요.
Celery Executor
Celery Executor의 경우 Dag 작성자는 Celery Workers에서 임의 코드를 실행할 수 있어요. 이는 같은 워커에서 실행되는 모든 tasks에 잠재적으로 영향을 줄 수 있다는 뜻이에요. Celery Executor로 Airflow를 실행한다면 Deployment Manager는 Dag 작성자가 이 기능을 남용하지 않을 것이라고 신뢰해야 하며, Cluster Policies로 큐로 task 실행을 분리하지 않는다면 task 사이에 격리가 없다고 가정해야 해요.
Kubernetes Executor
Kubernetes Executor의 경우 Dag 작성자는 자신이 실행되는 Kubernetes POD에서 임의 코드를 실행할 수 있어요. 각 task는 별도의 POD에서 실행되므로, 일반적으로 말해서 Kubernetes가 POD 사이에 격리를 제공하므로 task 사이에 이미 격리가 있어요.
Triggerer
Triggerer의 경우 Dag 작성자는 Triggerer에서 임의 코드를 실행할 수 있어요. 현재 deferrable 기능을 사용하는 tasks를 서로 격리할 수 있는 강제 메커니즘이 없으며, 다양한 tasks의 임의 코드가 같은 프로세스/머신에서 실행될 수 있어요. 기본 배포는 모든 팀의 triggers를 처리하는 단일 Triggerer 인스턴스를 실행해요 — 팀별 Triggerer 인스턴스에 대한 내장 지원이 없어요. 추가로 Triggerer는 JWT 인증을 잠재적으로 우회하고 메타데이터 데이터베이스에 직접 접근할 수 있는 인프로세스 Execution API 전송을 사용해요. multi-team 배포의 경우 Deployment Managers는 배포 수준 조치로 팀별로 별도의 Triggerer 인스턴스를 실행해야 하지만, 그래도 각 인스턴스는 잠재적으로 직접 데이터베이스 접근을 유지하며 그곳에서 실행되는 trigger 코드를 가진 Dag 작성자는 다른 팀에 속한 데이터를 포함해 데이터베이스에 직접 접근할 수 있어요. Deployment Manager는 Dag 작성자가 이 기능을 남용하지 않을 것이라고 신뢰해야 해요.
Scheduler와 API Server에 필요하지 않은 Dag 파일
Deployment Manager는 Scheduler와 API Server가 Dag Files에 조차 접근하지 못하게 해서 Dag 작성자가 제공하는 코드 실행을 격리할 수 있어요 — 특히 Scheduler와 API Server에서요. 일반적으로 말해 Dag 작성자가 제공하는 어떤 코드도 Scheduler나 API Server 프로세스에서 실행되어서는 안 돼요. 즉 배포 관리자는 Scheduler와 API Server에서 Dag bundles에 필요한 자격 증명을 제외할 수 있어요 — 하지만 bundles는 그 컴포넌트들에도 여전히 구성되어야 해요.
Scheduler와 API Server에서 선택된 코드를 실행하도록 Dag 작성자 허용
Dag 작성자가 사전 등록된 커스텀 코드를 Scheduler 또는 API Server 프로세스에서 실행하도록 선택할 수 있게 하는 여러 기능이 있어요 — 예를 들어 커스텀 Timetables, UI plugins, Connection UI Fields, Operator extra links, macros, listeners를 선택할 수 있어요 — 이러한 기능은 모두 Dag 작성자가 Scheduler 또는 API Server 프로세스에서 실행될 코드를 선택할 수 있게 해줘요. 하지만 이것은 Dag 작성자가 Dag bundles에 추가할 수 있는 임의 코드여서는 안 돼요. 모든 그러한 기능은 설치된 패키지에 의해서만 제공될 수 있는 코드를 실행할 수 있는 plugins와 providers 메커니즘을 통해서만 사용할 수 있어요(플러그인의 경우 Dag 작성자가 쓰기 접근을 가져서는 안 되는 PLUGINS 폴더에 추가될 수도 있어요). PLUGINS_FOLDER는 Airflow 1.10에서 온 레거시 메커니즘이에요 — 하지만 배포 관리자가 그러한 컨텍스트에서 실행될 코드를 효과적으로 선택하고 등록할 수 있게 해주는 entrypoint 메커니즘 사용을 권장해요. Dag 작성자는 Scheduler와 API Server에 설치된 패키지를 설치하거나 수정할 수 없으며, 이것이 Dag 작성자가 그 프로세스에서 임의 코드를 실행하는 것을 방지하는 방법이에요.
추가로, PLUGINS_FOLDER를 활용하고 구성하기로 결정했다면, Deployment Manager가 Dag 작성자가 이 폴더에 대한 쓰기 접근을 갖지 않도록 보장하는 것이 필수적이에요.
Deployment Manager는 Dag 작성자가 임의 코드를 실행하는 것을 방지하는 추가 제어 메커니즘을 도입하기로 결정할 수 있어요. 이것은 전적으로 Deployment Manager의 손에 있으며 다음 장에서 논의돼요.
모든 Dags에 대한 접근
모든 Dag 작성자는 Airflow 배포의 모든 Dags에 접근할 수 있어요. 즉 언제든 제한 없이 어떤 Dag이든 보고, 수정하고, 업데이트할 수 있어요.
JWT 인증과 워크로드 격리
Airflow는 공개 REST API와 내부 Execution API 모두에 JWT(JSON Web Token) 인증을 사용해요. JWT 인증 흐름, 토큰 구조, 구성에 대한 자세한 내용은 JWT 토큰 인증을 참고하세요. 워크로드 격리 보호의 현재 상태와 한계는 워크로드 격리와 현재 한계를 참고하세요.
아래 다이어그램은 Airflow 컴포넌트와 메타데이터 데이터베이스 사이의 신뢰 경계를 요약해요. 실선 화살표는 인증된 네트워크 호출(JWT를 담은 HTTP)이고, 점선 화살표는 직접 데이터베이스 접근이에요 — 점선 오른쪽의 컴포넌트는 메타데이터 DB를 읽고 쓸 수 있으므로, 그들이 실행하는 어떤 코드든 메타데이터 데이터베이스로 암묵적으로 신뢰돼요.
flowchart LR
subgraph users["Users (untrusted by default)"]
UI[UI / browser]
CLI[CLI]
EXT[External REST clients]
end
subgraph dataplane["Worker plane (no metadata DB access)"]
WRK[Worker / Task]
end
subgraph controlplane["Control plane (metadata DB access)"]
APISVR[API Server]
SCH[Scheduler]
DFP[Dag File Processor]
TRG[Triggerer]
end
DB[(Metadata DB)]
UI -->|JWT| APISVR
CLI -->|JWT| APISVR
EXT -->|JWT| APISVR
WRK -->|JWT<br/>Execution API| APISVR
APISVR -. SQL .-> DB
SCH -. SQL .-> DB
DFP -. SQL .-> DB
TRG -. SQL .-> DB
DFP -. in-process<br/>JWT bypassed .-> APISVR
TRG -. in-process<br/>JWT bypassed .-> APISVR
classDef untrusted fill:#ffcdd2,stroke:#c62828,stroke-width:2px,color:#000
classDef trusted fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#000
classDef data fill:#fff9c4,stroke:#f57f17,stroke-width:2px,color:#000
class UI,CLI,EXT,WRK untrusted
class APISVR,SCH,DFP,TRG trusted
class DB data
의도적인 비대칭: 워커는 직접 DB 자격 증명이 없고 JWT 인증된 Execution API를 통해서만 데이터에 도달하는 반면, DFP와 Triggerer는 컨트롤 플레인의 DB 접근을 공유하고 JWT bearer 의존성을 우회하는 인프로세스 전송을 사용해요. 이것이 Dag File Processor와 Triggerer가 사용자 제공 코드를 실행함에도 불구하고 컨트롤 플레인 신뢰 경계의 일부로 취급되는 이유예요 — 워크로드 격리와 현재 한계와 아래의 한계를 참고하세요.
라우터 수준의 심층 방어 (Defense in depth at the router level)
공개 REST API 라우터(/api/v2)와 UI 라우터(/ui)는 모두 라우터 수준에서 Depends(get_user)를 선언해요. 이것은 순전히 심층 방어 백스톱이에요: 모든 인증된 라우트는 이미 자체적으로 get_user를 해석하는 GetUserDep 또는 requires_access_* 의존성을 선언하고, FastAPI가 요청당 의존성 해석을 캐시하므로 중복 선언은 한 번만 해석되고 런타임 비용은 0이에요. 가치는 두 라우터 아래에 인증 확인 없이 미래 라우트가 추가되는 것을 방지하는 것이에요 — 라우터 수준 의존성이 등록 시점에 회귀를 잡아요. 명시적 무인증 예외는 monitor_router(health probes), version_router, 공개 auth_router(login 엔드포인트)로 제한되며, 이것들은 authenticated_router 아래가 아니라 공개 라우터에 직접 마운트돼요.
구조적 테스트는 두 라우터가 라우터 수준 Depends(get_user)를 담고 있음을 주장하므로, 목적을 고려하지 않고 의존성을 제거하는 리팩터는 무인증 표면을 조용히 넓히기보다 CI에서 실패해요.
현재 격리 한계
Airflow 3은 워커 task 코드가 메타데이터 데이터베이스에 직접 접근하는 것을 방지함으로써 보안 모델을 크게 개선했지만(워커는 이제 Execution API를 통해서만 통신), Dag 작성자 사이의 완벽한 격리는 아직 달성되지 않았어요. Dag 작성자 코드는 Dag File Processor와 Triggerer에서 직접 데이터베이스 접근으로 여전히 잠재적으로 실행돼요.
소프트웨어 가드 vs 의도적 접근 : Airflow는 Dag 작성자 코드의 우발적이고 의도하지 않은 직접 데이터베이스 접근을 방지하는 소프트웨어 수준 가드를 구현해요. Dag File Processor는 Dag 파일을 파싱하는 하위 프로세스를 포크하기 전에 데이터베이스 세션과 connection 정보를 제거하고, 워커 tasks는 Execution API만 사용해요.
하지만 이러한 소프트웨어 가드는 **의도적이고 악의적인 접근을 보호하지 못해요**. Dag 파일을 파싱하고 trigger 코드를 실행하는 하위 프로세스는 부모 프로세스(Dag File Processor 매니저와 Triggerer 각각)와 **같은 Unix 사용자**로 실행돼요. POSIX 프로세스 격리가 동작하는 방식 때문에, 같은 사용자로 실행되는 하위 프로세스는 여러 메커니즘으로 부모의 자격 증명을 검색할 수 있어요:
- **환경 변수 (Environment variables)**: 기본적으로 Linux에서 어떤 프로세스든 같은 사용자로 실행되는 다른 프로세스의 `/proc/<PID>/environ`을 읽을 수 있어요 — 그래서 환경 변수로 전달된 데이터베이스 자격 증명(예: `AIRFLOW__DATABASE__SQL_ALCHEMY_CONN`)은 부모 프로세스에서 읽을 수 있어요. 이것은 task의 supervisor에서 구현된 프로세스의 dumpable 속성을 설정해 방지할 수 있어요.
- **구성 파일 (Configuration files)**: 구성이 파일에 저장되면, 그 파일은 부모 프로세스가 읽을 수 있어야 하므로 같은 사용자로 실행되는 하위 프로세스도 읽을 수 있어요.
- **명령 기반 secrets** (`_CMD` 접미사 옵션): 하위 프로세스가 같은 명령을 실행해 secrets를 검색할 수 있어요.
- **Secrets manager 접근**: 부모가 secrets backend를 사용한다면, 하위는 프로세스 환경이나 파일시스템에서 사용 가능한 자격 증명을 사용해 같은 secrets manager에 접근할 수 있어요.
즉 의도적으로 악의적인 Dag 작성자는 데이터베이스 자격 증명을 검색하고 메타데이터 데이터베이스에 대한 **완전한 읽기/쓰기 접근**을 얻을 수 있어요 — 어떤 Dag, task instance, connection, variable이든 수정하는 능력을 포함해서요. 소프트웨어 가드는 우발적 접근(Airflow 2에서의 습관으로 `airflow.settings.Session`을 import하는 Dag 작성자 같은)을 다루지만, 결심한 행위자가 이를 우회하는 것을 막지는 못해요.
워커에서는 Deployment Manager가 워커 프로세스를 (환경 변수나 구성 어느 쪽으로든) 데이터베이스 자격 증명을 전혀 받지 않도록 구성하면 격리가 더 강해질 수 있어요. 워커는 수명이 짧은 JWT 토큰을 사용해 Execution API를 통해서만 통신해야 해요. 워커에서 실행되는 task는 — 그에게 접근 가능한 자격 증명이 없도록 구성되었을 때 — 진정으로 메타데이터 데이터베이스에 직접 접근해서는 안 돼요.
Dag File Processor와 Triggerer는 사용자 코드를 실행하지만 JWT 인증 우회에는 소프트 보호만 있음 : 사용자 코드를 실행하는 Dag File Processor와 Triggerer 프로세스는 Execution API에 접근하기 위해 인프로세스 전송을 사용하며, 이것은 JWT 인증을 우회해요. 이러한 컴포넌트들이 사용자 제출 코드(Dag 파일과 trigger 코드 각각)를 실행하므로, 이러한 컴포넌트에서 자신의 코드가 실행되는 Dag 작성자는 소프트 보호를 우회하면 유효한 JWT 토큰 없이 모든 Execution API 작업에 제한 없는 접근을 가져요 — 어떤 connection, variable, XCom이든 읽는 능력을 포함해서요.
추가로 Dag File Processor는 메타데이터 데이터베이스에 직접 접근할 수 있어요(직렬화된 Dags를 저장해야 하므로). 위에서 설명한 대로, Dag File Processor 컨텍스트에서 실행되는 Dag 작성자 코드는 부모 프로세스에서 데이터베이스 자격 증명을 검색하고, JWT 서명 키 구성이 프로세스 환경에 있다면 그것을 포함해 데이터베이스에 직접 접근할 수 있어요. Dag 작성자가 JWT 서명 키를 얻으면 임의 토큰을 위조할 수 있어요.
Dag File Processor와 Triggerer는 팀 간에 공유됨 : 기본 배포에서 단일 Dag File Processor 인스턴스가 모든 Dag 파일을 파싱하고 단일 Triggerer 인스턴스가 모든 triggers를 처리해요 — 팀 할당과 관계없이요. 팀별 Dag File Processor 또는 Triggerer 인스턴스를 실행하는 내장 지원이 없어요. 즉 서로 다른 팀의 Dag 작성자 코드가 같은 프로세스에서 실행되며, 인프로세스 Execution API와 직접 데이터베이스 접근을 잠재적으로 공유해요.
분리가 필요한 multi-team 배포의 경우 Deployment Managers는 배포 수준 조치로 **팀별로 별도의 Dag File Processor와 Triggerer 인스턴스**를 실행해야 해요(예: 각 인스턴스가 특정 팀에 속한 bundles만 처리하도록 구성함). 하지만 별도 인스턴스로도 각 Dag File Processor와 Triggerer는 메타데이터 데이터베이스에 대한 직접 접근을 잠재적으로 유지해요 — 이러한 컴포넌트에서 자신의 코드가 실행되는 Dag 작성자는 부모 프로세스에서 자격 증명을 검색하고, Deployment Manager가 Unix 사용자 수준 격리를 구현하지 않는 한 다른 팀에 속한 데이터를 읽거나 수정하는 것을 포함해 데이터베이스에 직접 접근할 수 있어요([개선된 격리를 위한 배포 강화](#deployment-hardening-for-improved-isolation) 참고).
Execution API의 워크로드 간 격리 없음
: 모든 워커 워크로드는 같은 키로 서명되고 같은 audience를 공유하는 토큰으로 같은 Execution API에 인증해요. ti:self 스코프 강제가 워커가 다른 task의 특정 엔드포인트(heartbeat, 상태 전이)에 접근하는 것을 방지하지만, connections, variables, XComs 같은 공유 리소스는 모든 tasks에 접근 가능해요. Execution API 수준에서 서로 다른 팀이나 Dag 작성자에 속한 tasks 사이의 격리는 없어요.
Execution API가 `ti:self` 외에 **강제하는** 것들:
- **Secrets-backend 거부가 존중됨.** Execution API가 connection 또는 variable 조회에 `401`/`403`을 반환하면, SDK의 `ExecutionAPISecretsBackend`는 `None`을 반환하는 대신 `AirflowSecretsBackendAccessDenied`(`PermissionError`의 하위 클래스)를 발생시켜요. 권위 있는 백엔드가 요청을 명시적으로 거부했을 때 secrets-backend 디스패처는 권한 검사를 하지 않는 `EnvironmentVariablesBackend` 같은 덜 제한적인 백엔드로 폴 스루해서는 안 돼요. `NOT_FOUND` 응답은 기존 폴 스루 동작을 유지해 not-found-here 경로가 계속 사용 가능하게 해요. 이것은 권한 제어가 발동했지만 그 거부가 miss로 취급되던 "Type C" 간극을 닫아요.
- **강화된 역직렬화 허용 목록.** 직렬화기의 클래스 이름 허용 목록 regex는 전체 문자열 일치를 요구하도록 고정되어, 공격자 제어 값이 비허용 이름에 허용된 접미사를 붙여 신뢰할 수 없는 클래스 이름을 몰래 넣을 수 없어요.
- **타입이 지정된 JWT 클레임 스키마.** 암호화 검증 후에도 클레임은 `scope` 리터럴을 강제하는 타입이 지정된 Pydantic 스키마(`TIClaims`)를 통과하고, `sub`를 UUID로 파싱하는 `TIToken`을 통과해요. `scope`가 알 수 없거나 `sub`가 유효한 UUID가 아닌 토큰은 어떤 라우트 핸들러가 실행되기 전에 거부돼요.
토큰 서명 키가 공유 비밀이 될 수 있음
: 대칭 키 모드([api_auth] jwt_secret)에서 같은 비밀 키가 토큰을 생성하고 검증하는 데 모두 사용돼요. 이 비밀에 접근할 수 있는 모든 컴포넌트는 다른 task instance를 위한 것이나 상위 스코프를 가진 토큰 등 임의 클레임으로 토큰을 위조할 수 있어요. 비밀이 배포 구성을 통해 api-server와 scheduler에만 제공된다면 이것은 시스템 보안에 영향을 주지 않아요.
민감한 구성 값이 로그로 누출될 수 있음
: Dag 작성자는 환경 변수나 구성 값을 task 로그로 출력하는 코드(예: print(os.environ))를 작성할 수 있어요. Airflow는 로그에서 알려진 민감한 값을 마스킹하지만, 마스킹은 값 패턴을 인식하는 데 달려 있어요. 환경 변수를 의도적으로 또는 우발적으로 로그하는 Dag 작성자는 데이터베이스 자격 증명, JWT 서명 키, Fernet 키 또는 다른 secrets를 task 로그에 노출할 수 있어요. Deployment Managers는 task 로그에 대한 접근을 제한하고 민감한 구성이 필요로 하는 컴포넌트에만 제공되도록 해야 해요(아래 민감한 변수 표 참고).
개선된 격리를 위한 배포 강화
Dag 작성자와 팀 사이에 더 강한 격리가 필요한 Deployment Managers는 다음 조치를 취할 수 있어요. 이들은 Airflow의 내장 보안 모델을 넘어서는 배포별 조치로, Airflow는 이들을 네이티브로 강제하지 않아요.
Dag 파일의 필수 코드 검토 : Dag bundles에 대한 모든 Dag 제출에 대한 검토 프로세스를 구현하세요. 여기에는 다음이 포함될 수 있어요:
- Dag 파일이 배포되기 전에 pull request 검토 요구.
- 의심스러운 패턴(예: 직접 데이터베이스 접근 시도, 환경 변수 읽기, 구성 모듈 import)을 감지하는 Dag 코드 정적 분석.
- 잠재적으로 위험한 코드에 플래그를 지정하는 자동화된 lint 규칙.
민감한 구성을 필요로 하는 컴포넌트로 제한 : 모든 구성 파라미터를 모든 컴포넌트가 공유하지 마세요. 특히:
- JWT 서명 키(`[api_auth] jwt_secret` 또는 `[api_auth] jwt_private_key_path`)는 토큰을 생성해야 하는 컴포넌트(Scheduler/Executor, API Server)와 토큰을 검증해야 하는 컴포넌트(API Server)에만 제공되어야 해요. 워커는 서명 키에 접근해서는 안 돼요 — 그들은 제공된 토큰만 필요해요.
**예외 — 원격 edge 워커.** `edge3` provider의 워커는 자체 API 요청에 서명하므로, edge 워커를 실행하는 모든 호스트에 `[api_auth] jwt_secret`이 프로비저닝되어야 해요. 그 경로에 비대칭 대안이 없고, 워커가 대신 API server가 발급한 토큰을 받는 배치도 없어요: 워커는 어떤 워크로드든 받기 전에 Edge API에 인증해야 하므로 그 시점에 이미 자격 증명을 보유해야 해요. 호스트에 서명 키를 프로비저닝하는 것이 그 부트스트랩을 충족시키는 방법이에요. 대칭 모드에서 이것은 사용자 JWT와 Execution API task 토큰에 서명하는 데 사용되는 것과 같은 비밀이라, edge 워커를 실행하는 배포는 위의 지침을 충족할 수 없고, 아래 *토큰 서명 키가 공유 비밀이 될 수 있음*의 보증(비밀이 API server와 scheduler에만 제공된다는 조건)은 edge 워커에 대해 성립하지 않아요. Deployment Managers는 edge 워커 호스트를 컨트롤 플레인 신뢰 경계의 일부로 취급하거나, 어떤 edge 워커 호스트의 손상이 배포의 토큰 서명 능력 손상과 동일함을 받아들여야 해요.
- 외부 시스템에 대한 connection 자격 증명(Secrets Managers를 통해)은 (Execution API를 통해 워커에게 제공하는) API Server에만 제공되어야 하고, Scheduler, Dag File Processor, Triggerer 프로세스에는 직접 제공되지 않아야 해요. 하지만 이것은 Deadline Alerts나 외부 시스템에 인증해야 하는 triggers 같은 Airflow의 일부 기능을 제한해요.
- 데이터베이스 connection 문자열은 직접 데이터베이스 접근이 필요한 컴포넌트(API Server, Scheduler, Dag File Processor, Triggerer)에만 제공되어야 하고, 워커에는 제공되지 않아야 해요.
구성을 환경 변수로 전달
: 보안을 높이려면 민감한 구성 값을 구성 파일이 아니라 환경 변수로 전달하세요. 환경 변수는 Airflow 워커 프로세스에서 내장 보호 덕분에 구성 파일보다 본질적으로 더 안전해요: Linux에서 supervisor 프로세스가 task 프로세스를 포크하기 전에 prctl(PR_SET_DUMPABLE, 0)을 호출하고, 이 플래그는 포크된 자식에게 상속돼요. 이것은 두 프로세스를 non-dumpable로 표시해, 같은 UID의 형제 프로세스가 /proc/<pid>/environ, /proc/<pid>/mem을 읽거나 ptrace로 붙는 것을 방지해요. 반대로 디스크의 구성 파일은 같은 Unix 사용자로 실행되는 어떤 프로세스든 읽을 수 있어요. 환경 변수는 개별 프로세스나 컨테이너로 범위를 지정할 수도 있어, 어떤 컴포넌트가 어떤 secrets에 접근하는지 제한하기 더 쉬워요.
아래 다이어그램과 표는 잘 강화된 배포에서 어떤 컴포넌트가 어떤 종류의 민감한 값이 필요한지 요약해요. 워커는 DB 자격 증명이나 JWT 서명 키를 전혀 보면 안 되고, API Server는 권한 있는 값 *전부*가 필요한 유일한 컴포넌트예요. 컴포넌트는 최소 권한(Worker, 녹색)에서 최대 권한(API Server, 파란색)으로 배열되어 있어요 — 커지는 열은 시각적으로 "더 많은 권한 ⇒ 더 많은 secrets"을 전달해요:
```
flowchart LR
subgraph WRK["Worker (least privileged)"]
direction TB
W1[Fernet key]
W2[Worker secrets backend credentials]
W3[Remote log handler kwargs]
end
subgraph TRG["Triggerer"]
direction TB
T1[DB connection]
T2[Fernet key]
T3[Non-worker secrets backend credentials]
T4[Remote log handler kwargs]
end
subgraph DFP["Dag File Processor"]
direction TB
D1[DB connection]
D2[Fernet key]
D3[Non-worker secrets backend credentials]
end
subgraph SCH["Scheduler"]
direction TB
S1[DB connection]
S2[JWT signing key]
S3[Fernet key]
S4[Non-worker secrets backend credentials]
S5[Remote log handler kwargs]
end
subgraph API["API Server (most privileged)"]
direction TB
A1[DB connection]
A2[JWT signing key]
A3[Fernet key]
end
classDef wrk fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#000
classDef ctrl fill:#bbdefb,stroke:#1565c0,stroke-width:2px,color:#000
class WRK wrk
class API,SCH,DFP,TRG ctrl
```
행별로 읽어 어떤 컴포넌트가 주어진 secret이 필요한지 확인하고, 열별로 읽어 한 컴포넌트가 무엇을 볼 수 있는지 확인하세요:
| Sensitive value | API Server | Scheduler | Dag File Processor | Triggerer | Worker |
| --- | --- | --- | --- | --- | --- |
| DB connection | ✓ | ✓ | ✓ | ✓ | — |
| JWT signing key | ✓ | ✓ | — | — | — |
| Fernet key | ✓ | ✓ | ✓ | ✓ | ✓ |
| Secrets backend credentials (non-worker) | — | ✓ | ✓ | ✓ | — |
| Secrets backend credentials (worker) | — | — | — | — | ✓ |
| Remote log handler kwargs | — | ✓ | — | ✓ | ✓ |
다음 표는 모든 보안에 민감한 구성 변수(Airflow 구성에서 `sensitive: true`로 표시됨)를 나열해요. Deployment Managers는 각 변수를 검토하고 그것이 필요한 컴포넌트에만 제공되도록 해야 해요. "Needed by" 열은 어떤 컴포넌트가 보통 그 변수를 요구하는지 나타내요 — 하지만 실제 필요는 특정 배포 토폴로지와 사용 중인 기능에 달려 있어요.
**핵심 Airflow 민감한 구성 변수:**
| Environment variable | Description | Needed by |
| --- | --- | --- |
| `AIRFLOW__API_AUTH__JWT_SECRET` | JWT signing key (symmetric mode) | API Server, Scheduler |
| `AIRFLOW__API__SECRET_KEY` | API secret key for log token signing | API Server, Scheduler, Workers, Triggerer |
| `AIRFLOW__CORE__ASSET_MANAGER_KWARGS` | Asset manager credentials | Dag File Processor |
| `AIRFLOW__CORE__FERNET_KEY` | Fernet encryption key for connections/variables at rest | API Server, Scheduler, Workers, Dag File Processor, Triggerer |
| `AIRFLOW__DATABASE__SQL_ALCHEMY_CONN` | Metadata database connection string | API Server, Scheduler, Dag File Processor, Triggerer |
| `AIRFLOW__DATABASE__SQL_ALCHEMY_CONN_ASYNC` | Async metadata database connection string | API Server, Scheduler, Dag File Processor, Triggerer |
| `AIRFLOW__DATABASE__SQL_ALCHEMY_ENGINE_ARGS` | SQLAlchemy engine parameters (may contain credentials) | API Server, Scheduler, Dag File Processor, Triggerer |
| `AIRFLOW__LOGGING__REMOTE_TASK_HANDLER_KWARGS` | Remote logging handler credentials | Scheduler, Workers, Triggerer |
| `AIRFLOW__SECRETS__BACKEND_KWARGS` | Secrets backend credentials (non-worker mode) | Scheduler, Dag File Processor, Triggerer |
| `AIRFLOW__SENTRY__SENTRY_DSN` | Sentry error reporting endpoint | Scheduler, Triggerer |
| `AIRFLOW__WORKERS__SECRETS_BACKEND_KWARGS` | Worker-specific secrets backend credentials | Workers |
`AIRFLOW__API_AUTH__JWT_PRIVATE_KEY_PATH`(비대칭 서명용 JWT 개인 키 경로)는 파일 경로이고 secret 값 자체가 아니므로 config.yml에서 `sensitive`로 표시되지 않는다는 점을 유의하세요. 하지만 그것이 가리키는 파일에 대한 접근은 (토큰을 생성하는) Scheduler와 (검증하는) API Server로 제한되어야 해요.
**Provider별 민감한 구성 변수:**
다음 변수들은 Airflow providers로 정의되며, 해당 provider 기능이 필요한 컴포넌트에만 설정되어야 해요. 어떤 컴포넌트가 이러한 변수를 요구하는지에 대한 결정은 Deployment Manager가 각 컴포넌트에서 어떤 providers와 기능을 활성화할지에 대한 선택에 달려 있어요.
| Environment variable | Provider | Description |
| --- | --- | --- |
| `AIRFLOW__CELERY_BROKER_TRANSPORT_OPTIONS__SENTINEL_KWARGS` | celery | Sentinel kwargs |
| `AIRFLOW__CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS__SENTINEL_KWARGS` | celery | Sentinel kwargs |
| `AIRFLOW__CELERY__BROKER_URL` | celery | Broker url |
| `AIRFLOW__CELERY__FLOWER_BASIC_AUTH` | celery | Flower basic auth |
| `AIRFLOW__CELERY__RESULT_BACKEND` | celery | Result backend |
| `AIRFLOW__KEYCLOAK_AUTH_MANAGER__CLIENT_SECRET` | keycloak | Client secret |
| `AIRFLOW__OPENSEARCH__PASSWORD` | opensearch | Password |
| `AIRFLOW__OPENSEARCH__USERNAME` | opensearch | Username |
Deployment Managers는 전체 구성 참조를 검토하고 특정 배포와 관련된 자격 증명이나 secrets를 담은 추가 파라미터를 식별해야 해요.
JWT 서명에 비대칭 키 사용
: 비대칭 키([api_auth] jwt_private_key_path와 JWKS 엔드포인트)를 사용하면 대칭 키보다 더 나은 보안을 제공해요. 왜냐하면:
- (서명에 사용되는) 개인 키를 Scheduler/Executor로 제한할 수 있어요.
- API Server는 검증을 위해 (JWKS를 통한) 공개 키만 필요해요.
- 워커가 JWKS 엔드포인트에 접근할 수 있어도 개인 키가 없으므로 토큰을 위조할 수 없어요.
네트워크 수준 격리 : 네트워크 정책, VPC 또는 유사한 메커니즘을 사용해 어떤 컴포넌트가 서로 통신할 수 있는지 제한하세요. 예를 들어 워커는 Execution API 엔드포인트에만 도달할 수 있어야 하고, 메타데이터 데이터베이스나 내부 서비스에는 직접 도달할 수 없어야 해요. Unix 사용자 수준 격리가 구현된다면 Dag File Processor와 Triggerer 하위 프로세스는 이상적으로 메타데이터 데이터베이스에 대한 네트워크 접근도 갖지 않아야 해요.
기타 조치와 향후 개선 : Deployment Managers는 보안 요구사항에 따라 추가 조치를 구현해야 할 수 있어요. 여기에는 Execution API 접근 패턴 모니터링·감사, Dag 코드의 런타임 샌드박싱, 팀별 전용 인프라가 포함될 수 있어요.
향후 Airflow 버전은 두 가지 접근 방식으로 이러한 한계를 해결할 계획이에요:
- **전략적 (장기)**: Dag File Processor와 Triggerer가 (오늘날 워커가 Execution API를 사용하는 것처럼) API server를 통해서만 메타데이터 데이터베이스와 통신하도록 이동. 이는 이러한 컴포넌트가 데이터베이스 자격 증명을 전혀 가질 필요를 없애, 배포 수준 조치에 기대는 대신 설계로 보안을 제공할 거예요.
- **전술적 (단기)**: Dag File Processor와 Triggerer 하위 프로세스에서 Unix 사용자 impersonation의 네이티브 지원. Dag 작성자 코드가 부모의 자격 증명이나 데이터베이스에 접근할 수 없는 다른 낮은 권한 사용자로 실행되도록요.
Airflow 커뮤니티는 이러한 개선을 적극적으로 작업하고 있어요.
커스텀 RBAC 한계
Airflow에 정의된 RBAC가 특정 UI 사용자의 특정 Dags와 기능에 대한 접근을 제한할 수 있지만, 커스텀 역할과 권한에 관해서는 일부 권한이 개별 Dags에 대한 접근(또는 그 부재)을 덮어쓸 수 있어요. 예를 들어 감사 로그 권한은 가진 사용자가 명시적으로 접근 권한이 없는 Dags의 로그까지 모두 볼 수 있게 해요. 이것은 커스텀 RBAC 역할을 만들 때 Deployment Manager가 알아야 할 사항이에요.
Assets를 통한 Dags 트리거
Assets를 통한 Dags 트리거는 asset materialization이 Dag을 트리거할 수 있게 하는 기능이에요. 이 기능은 사용자에게 Dags를 수동으로 트리거하는 특정 접근 권한을 주지 않고 Dags를 트리거할 수 있게 설계됐어요. "Trigger Dag" 권한은 UI나 API를 통해 Dags를 수동으로 트리거하는 것에만 영향을 주며, Assets를 통한 Dags 트리거에는 영향을 주지 않아요. Dag 작성자는 특정 assets가 Dags를 트리거하도록 명시적으로 허용하며, 그 assets를 만들 수 있는 능력이 있는 사람에게 Assets를 통한 Dags 트리거 능력을 부여해요.
Deployment Managers의 책임
Deployment Manager로서 Dag 작성자의 기능을 인식하고 그들이 가진 기능을 남용하지 않을 것이라고 신뢰해야 해요. 또한 Scheduler와 API Server 프로세스에서 Dag 작성자가 임의 코드를 실행하는 것을 방지하도록 Airflow 설치를 적절히 구성했는지 확인해야 해요.
Airflow 설치 배포와 보호
Deployment Managers는 또한 Airflow를 배포하고, Airflow가 배포되는 조직에 적용 가능한 안전한 배포 모범 사례를 따르는 방식으로 사용자에게 접근 가능하게 만들 책임이 있어요. 여기에는 다음이 포함되지만 국한되지는 않아요:
- TLS/VPC 및 조직이 요구하는 어떤 네트워크 보안이든 통신 보호
- 웹 애플리케이션에 보통 적용되는 rate-limiting과 다른 형태의 보호 적용
- 알려지고 인가된 사용자만 Airflow에 접근할 수 있도록 웹 애플리케이션에 인증과 권한 부여 적용
- 비정상 활동 탐지와 그에 대한 보호
- 올바른 세션 백엔드 선택과 세션 타임아웃을 포함한 적절한 구성
Dag 작성자 기능 제한
Deployment Manager는 Dag 작성자가 임의 코드를 실행하는 것을 방지하기 위한 추가 메커니즘을 사용할 수도 있어요 — 예를 들어 코드가 배포되기 전에 검토하고, 정적으로 검사하며, 악성 코드 제출을 방지하는 다른 방법을 추가할 수 있는 Dag 제출 주변 도구를 도입할 수 있어요. Dag bundle에 코드를 제출하는 방법과 보호 방식은 전적으로 Deployment Manager에게 달려 있어요 — Airflow는 그것에 관한 어떤 도구나 메커니즘도 제공하지 않으며, Deployment Manager가 Dag bundles에 대한 접근을 보호하고 신뢰되는 코드만 제출되도록 도구를 제공할 것을 기대해요.
Airflow는 이러한 기능 중 어느 것도 네이티브로 구현하지 않으며, 외부 인프라 구성 요소로서 배포를 보호하는 데 필요한 모든 인프라를 배포하도록 배포 관리자에게 위임해요.
인증된 UI 사용자 접근 제한
Deployment Managers는 또한 접근 수준을 결정하고 사용자가 일으킬 수 있는 잠재적 피해를 이해해야 해요. 일부 Deployment Managers는 인증된 UI 사용자에 대한 세분화된 권한으로 접근을 더 제한할 수 있어요. 하지만 이러한 제한은 기본 Airflow 보안 모델 밖에 있으며 Deployment Managers의 재량에 있어요.
세분화된 접근 제어의 예시는 다음을 포함하지만 국한되지는 않아요:
- 로그인 권한 제한: 사용자가 로그인할 수 있는 계정을 제한하고, 특정 계정이나 역할만 Airflow 시스템에 접근하도록 허용.
- 뷰나 Dags에 대한 접근 제한: 특정 뷰나 특정 Dags에 대한 사용자 접근을 통제해, 사용자가 인가된 컴포넌트만 보고 상호작용할 수 있게 보장.
미래: multi-team 격리
이 예시들은 Deployment Managers가 Airflow 내에서 사용자 권한을 다듬고 제한할 수 있는 방법을 보여주며, 더 단단한 통제를 제공하고 역할과 책임에 따라 사용자가 필요한 컴포넌트와 기능에만 접근하도록 보장해요. 하지만 세분화된 접근 제어는 아직 서로 다른 사용자 그룹 사이의 완전한 격리와 접근 분리를 제공하지 않아요.
실험적 multi-team 기능([core] multi_team)은 팀 간 격리를 향한 한 걸음이지만, 현재는 UI와 REST API 수준에서만 팀 기반 격리를 강제해요. task 수준 격리는 아직 보장되지 않아요 — 서로 다른 팀의 워크로드가 같은 Execution API, JWT 서명 키, connections, variables, XComs에 대한 접근을 공유해요. ( 개선된 격리를 위한 배포 강화에 설명된) 추가 강화 조치가 구현되지 않은 배포에서는 한 팀에 속한 task가 다른 팀의 tasks에 사용 가능한 공유 리소스에 잠재적으로 접근할 수 있어요. multi-team 기능을 활성화하는 Deployment Managers는 task 실행 계층에서 팀 간의 보안에 중요한 격리를 위해 그것만 의존해서는 안 돼요 — 팀 간 분리를 보장하도록 구성하는 것은 Deployment Managers의 구성과 배포 보안에 대한 깊은 이해가 필요해요.
향후 Airflow 버전은 팀 스코프 Execution API 강제, 더 세분화된 JWT 토큰 스코프, 사용자 제출 코드의 더 나은 샌드박싱을 포함해 task 수준 격리를 개선할 거예요. Airflow 커뮤니티는 이러한 개선을 적극적으로 작업하고 있어요.
보안 취약점으로 간주되지 않는 것
다음 시나리오는 Airflow에서 보안 취약점으로 간주되지 않아요. 그것들은 의도적인 설계 선택, 위에서 설명한 신뢰 모델의 결과, 또는 Airflow의 위협 모델 밖에 속하는 문제예요. 보안 연구자(및 보안 분석을 수행하는 AI 에이전트)는 Airflow 보안 팀에 문제를 보고하기 전에 이 섹션을 검토해야 해요.
보고 정책에 대한 전체 세부 사항은 Airflow의 보안 정책을 참고하세요.
Dag 작성자가 임의 코드를 실행하는 것
Dag 작성자는 워커, Dag File Processor, Triggerer에서 임의 코드를 실행할 수 있어요. 여기에는 자격 증명, 환경 변수, (Dag File Processor와 Triggerer의 경우) 잠재적으로 메타데이터 데이터베이스 직접 접근이 포함돼요. 이것은 Dag 작성자의 기능에 설명된 의도된 동작이에요 — Dag 작성자는 신뢰된 사용자예요. Dag 작성자가 Dag 코드를 작성해 "RCE를 달성"하거나 "데이터베이스에 접근"할 수 있다는 보고는 문서화된 기능을 다시 말하는 것이지, 취약점을 발견한 것이 아니에요.
Dag 작성자 코드가 operators와 hooks에 소독되지 않은 입력을 전달하는 것
Dag 작성자가 소독되지 않은 UI 사용자 입력(Dag run 파라미터, variables, connection 구성 값 등)을 operators, hooks, 제3자 라이브러리에 전달하는 코드를 작성할 때, 책임은 Dag 작성자에게 있어요. Airflow의 hooks와 operators는 낮은 수준의 인터페이스이며 — Dag 작성자는 입력을 이러한 인터페이스에 전달하기 전에 소독해야 하는 Python 프로그래머예요.
SQL 인젝션이나 명령 인젝션은 비-Dag 작성자 사용자 역할(예: 인증된 UI 사용자)이 Dag 작성자가 그 입력을 안전하지 않게 전달하는 코드를 고의로 작성하지 않고 트리거할 수 있을 때만 취약점으로 간주돼요. 인젝션을 악용하는 유일한 방법이 Dag 파일을 작성하거나 수정하는 것이라면, 그것은 취약점이 아니에요 — Dag 작성자는 이미 임의 코드를 실행할 능력이 있으니까요. SQL 인젝션도 참고하세요.
공식 Airflow 문서가 인젝션으로 이어지는 패턴을 명시적으로 권장하는 예외가 존재해요 — 그 경우 문서 지침 자체가 문제이며 권고(advisory)를 받을 수 있어요.
Dag File Processor와 Triggerer가 잠재적으로 데이터베이스 접근을 가지는 것
Dag File Processor는 직렬화된 Dags를 저장하기 위해 잠재적으로 직접 데이터베이스 접근을 가져요. Triggerer는 trigger 상태를 관리하기 위해 잠재적으로 직접 데이터베이스 접근을 가져요. 두 컴포넌트 모두 사용자 제출 코드(Dag 파일과 trigger 코드 각각)를 실행하고 인프로세스 Execution API 전송으로 JWT 인증을 잠재적으로 우회해요. 이것들은 의도적인 아키텍처 선택이지 취약점이 아니에요. JWT 인증과 워크로드 격리에 문서화되어 있어요.
워커가 공유 Execution API 리소스에 접근하는 것
워커 tasks는 JWT 토큰을 사용해 Execution API를 통해 connections, variables, XComs에 접근할 수 있어요. ti:self 스코프가 교차 task 상태 조작을 방지하지만, 공유 리소스는 모든 tasks에 접근 가능해요. 이것은 현재 설계이지 취약점이 아니에요. "task가 다른 팀의 connection을 읽을 수 있다"는 보고는 JWT 인증과 워크로드 격리에 문서화된 현재 격리 모델의 알려진 한계를 설명하는 것이에요.
Execution API 토큰이 취소 가능하지 않은 것
워커에 발급된 Execution API 토큰은 자동 갱신과 함께 수명이 짧고(기본 10분), 의도적으로 취소 대상이 아니에요. 이것은 JWT 토큰 인증에 문서화된 설계 선택이지, 누락된 보안 컨트롤이 아니에요.
Deployment Managers는 두 속성을 별도로가 아니라 함께 읽어야 해요. 갱신은 자체 클레임에서 토큰을 재발급하므로, 토큰을 계속 제시하는 보유자는 계속 연장해요: 명목 수명은 사용을 멈춘 보유자만 경계해요. 취소가 없다는 것과 결합하면, (로그, 프로세스 목록, 손상된 워커를 통해) 누출된 토큰은 보유자가 계속 제시하는 한 사용 가능하며 철회할 수 없어요. 10분이라는 수치는 누출 후 노출에 대한 경계가 아니에요.
갱신도 task의 라이프사이클에 묶여 있지 않아요: 재발급 경로는 토큰의 서명과 만료를 검증하지만 데이터베이스를 참조하지 않아, 이미 끝났거나 행이 아카이브된 task instance를 이름으로 하는 토큰도 여전히 갱신돼요. 이 특정 동작에 대한 보고는 취약점보다는 강화 기회로 취급되며, 재발급을 task 상태에 묶는 기여는 일반 과정을 통해 환영해요. 자격 증명 수명에 대한 단단한 경계가 필요한 배포는 토큰 만료보다는 워커의 네트워크 격리에 의존해야 해요.
Connection 구성 기능
Connection 구성 역할을 가진 사용자는 임의 자격 증명과 connection 파라미터로 connections를 구성할 수 있어요. test connection 기능이 활성화되면, 이러한 사용자는 connection 파라미터를 통해 RCE, 임의 파일 읽기, Denial of Service를 트리거할 수 있어요. 이것은 설계상이에요 — connection 구성 사용자는 매우 권한이 있으며 이러한 기능을 남용하지 않도록 신뢰받아야 해요. test connection 기능은 Airflow 2.7.0부터 기본적으로 비활성화되어 있으며, 활성화는 이러한 위험을 인정하는 명시적인 Deployment Manager 결정이에요. 자세한 내용은 Connection 구성 사용자를 참고하세요.
이 허용의 경계는 워커 컨텍스트 실행과 역할이 쓸 수 있는 connection 값이에요. Connection 구성 사용자가 워커에서 코드가 실행되게 하거나, connection 파라미터를 통해 클라이언트 라이브러리의 동작에 영향을 주는 것은 문서화된 기능을 행사하는 것이지 취약점이 아니에요 — 메커니즘이 인젝션처럼 보여도요.
이 허용이 포함하지 않는 것은 역할이 읽을 자격이 없는 자격 증명 재료를 얻는 것이나, 역할이 실행 권한이 없는 컴포넌트에 도달하는 것이에요. 역할은 저장된 자격 증명에 쓰기 전용 접근을 가지므로, connection 구성 사용자가 secret을 복구할 수 있게 하거나 어떤 이유로 코드가 워커가 아닌 Scheduler나 API server에서 실행되게 하는 결함은 경계를 넘어 취약점이에요. 구별하는 질문은 어느 쪽이든 권한이 있는 역할이 아니라, 결함이 역할이 이미 얻을 수 없는 것을 산출하는지 여부예요.
인증된 사용자의 Denial of Service
Airflow는 공개 인터넷의 신뢰되지 않은 사용자에게 노출되도록 설계되지 않았어요. Airflow UI와 API에 접근할 수 있는 모든 사용자는 인증되고 알려져 있어요. 인증된 사용자가 트리거하는 Denial of Service 시나리오(매우 큰 Dag runs 만들기, 비싼 쿼리 제출, API 넘치게 하기 등)는 보안 취약점으로 간주되지 않아요. 그것들은 rate limiting, 리소스 할당량, 모니터링으로 배포 관리자가 해결해야 하는 운영 문제예요 — 모든 내부 애플리케이션에 대한 표준 조치예요. Airflow 설치 배포와 보호를 참고하세요.
인증된 사용자의 Self-XSS
유일한 피해자가 페이로드를 주입한 사용자 자신인 저장 XSS(Cross-site scripting) 시나리오(self-XSS)는 보안 취약점으로 간주되지 않아요. Airflow의 사용자는 인증되고 알려져 있으며, self-XSS는 공격자가 다른 사용자를 손상시킬 수 없어요. 낮은 권한 사용자가 그 사용자의 행동 없이 높은 권한 사용자의 세션에서 실행되는 페이로드를 주입할 수 있는 XSS 시나리오를 발견하면, 그것은 유효한 취약점이며 보고해야 해요.
Simple Auth Manager
Simple Auth Manager는 개발과 테스트 전용으로 의도됐어요. 이것은 명확히 문서화되어 있고 로그인 페이지에 눈에 띄는 경고 배너가 표시돼요. Simple Auth Manager에 특정된 보안 문제(약한 비밀번호 처리, rate limiting 부재, CSRF 보호 부재 등)는 프로덕션 보안 취약점으로 간주되지 않아요. 프로덕션 배포는 프로덕션급 auth manager를 사용해야 해요.
Docker 이미지의 제3자 의존성 취약점
Airflow의 reference Docker 이미지는 릴리스 시 최신 사용 가능한 의존성으로 빌드돼요. CVE 데이터베이스에 대해 이러한 이미지를 스캔해 발견된 취약점은 새 CVEs가 게시되면서 시간이 지남에 따라 나타날 것으로 예상돼요. 이러한 취약점은 Airflow 보안 팀에 보고해서는 안 돼요. 대신 사용자는 Docker 이미지 문서에 설명된 대로 업데이트된 의존성으로 자신만의 이미지를 빌드해야 해요.
제3자 의존성 취약점이 Airflow에서 실제로 악용 가능하다고 발견한다면(Airflow 맥락에서 악용을 입증하는 proof-of-concept 포함), 그것은 유효한 보고이며 보안 정책에 따라 제출해야 해요.
인간 검증 없는 자동화 스캔 결과
Airflow의 보안 모델에 대해 인간 검증 없이 findings를 나열하는 자동화 보안 스캐너 보고서는 유효한 취약점 보고로 간주되지 않아요. Airflow의 신뢰 모델은 전형적인 웹 애플리케이션과 크게 다르며 — 많은 스캐너 findings("admin 사용자가 코드를 실행할 수 있음"이나 "구성에서 데이터베이스 자격 증명 접근 가능" 같은)는 예상 동작이에요. 보고서는 이 문서에 설명된 보안 모델을 위반하는 방법을 입증하는 proof-of-concept을 포함해야 하며, 관련된 특정 사용자 역할과 공격 시나리오를 식별해야 해요.
클라이언트 라이브러리와 호출자가 제공한 값
Apache Airflow Python Client와 프로젝트가 게시하는 다른 클라이언트 라이브러리는 라이브러리예요. 그것들은 호출 애플리케이션이 제공하는 값에 따라 동작하며, 하나의 호출자가 제공한 문자열이 신뢰되고 다른 것은 그렇지 않다고 결정할 근거가 없어요.
임베딩 애플리케이션이 신뢰되지 않거나 잘못된 입력(공격자 영향 경로 파라미터, 잘못된 유형의 구성 값, 원격 호스트에 대해 선택된 평문 스키마, 클라이언트가 구성된 후 변경된 구성)을 전달해 클라이언트 라이브러리가 오작동하게 할 수 있다는 보고는 호출 애플리케이션이 내린 결정을 설명해요. 신뢰 경계는 라이브러리가 아닌 그 애플리케이션에 있어요. 이것은 Dag 작성자가 operators와 hooks에 소독되지 않은 입력을 전달하는 것에 대해 취한 입장과 같아요.
호출 애플리케이션이 올바르게 제공한 입력에 대해 클라이언트 라이브러리가 오작동할 때 — 예를 들어 라이브러리 자체가 안전하다고 문서화한 값이 잘못 처리되거나, 호출자가 설정한 보안 관련 설정을 라이브러리가 조용히 무시할 때 —의 결함은 범위 안에 있어요.
실험적 및 알파 기능
프로젝트가 실험적, 알파, 또는 미리보기로 문서화한 기능은 아직 보안 보장을 수반하지 않으며, 그것들에 국한된 결함은 프로덕션 보안 취약점으로 취급되지 않아요. 이것은 작성 시점에 multi-team 모드와 provider 문서에서 알파로 표시된 executors를 포함해요.
그러한 결함은 여전히 수정할 가치가 있으며 공개 기여로 환영받아요; 단지 보안 프로세스를 통해 처리되지 않고 권고가 발급되지 않을 뿐이에요. 기능이 실험적 상태를 벗어나면, 이전에 연기된 관련 문제는 그 시점에 장점을 바탕으로 재평가되어야 해요.
요청 크기 제한과 다른 역방향 프록시 책임
Airflow의 API server는 ASGI 애플리케이션이에요. 그런 애플리케이션처럼 핸들러에 디스패치하기 전에 요청 body를 메모리로 읽고, 요청 크기에 제한을 두지 않아요. 요청 크기, connection 비율, 총 동시 connection 수를 경계 짓는 것은 종료 역방향 프록시 또는 ingress의 책임이에요 — nginx의 client_max_body_size, Envoy의 max_request_bytes, 또는 배포의 선택된 로드 밸런서에서의 동등물이요.
인증되지 않은 클라이언트가 큰 요청 body를 보내(로그인 라우트처럼 자격 증명 없이 도달할 수 있어야 하는 엔드포인트를 포함해) API server가 메모리를 할당하게 할 수 있다는 보고는 이 속성을 설명해요. Airflow에서 취약점으로 취급되지 않아요. 이것은 요청이 인증되었는지 여부와 무관하게 성립한다는 점에 유의하세요: 위의 인증된 사용자의 Denial of Service carve-out과는 다른 점이며, 인증되지 않은 요청에도 적용돼요.
Deployment Managers는 proxy 계층에서 요청 크기 제한이 구성되도록 해야 해요. Airflow는 신뢰되지 않은 네트워크에 직접 노출되도록 의도되지 않았어요.
수명 종료(EOL) Airflow 버전에서만 도달할 수 있는 문제
보안 권고는 여전히 릴리스를 받는 릴리스 라인에 대해 발급돼요. 수명 종료에 도달한 라인에서만 도달할 수 있는 결함은 보안 프로세스를 통해 처리되지 않아요. 수정을 실을 릴리스가 없기 때문이에요.
이것은 그 라인에 여전히 설치되는 provider 패키지가 영향받는 코드를 포함하더라도 성립해요: 수명 종료 코어를 계속 지원하는 provider는 그 코어에 대한 보안 지원을 연장하지 않아요. 수명 종료 라인의 사용자는 업그레이드해야 해요.
Params와 스키마 검증
Airflow의 params 스키마 검증은 정확성과 사용성 기능이에요. 보안 컨트롤이 아니며, 어떤 신뢰 경계를 강제하는 데 의존되지 않아요.
특히, params를 선언하지 않은 operator는 그 필드에 대해 검증을 선택 해제한 것이에요; 필드가 비어 있을 것이라고 주장하지 않았어요. 스키마가 선언되지 않은 곳에 검사되지 않은 데이터가 제공될 수 있다는 보고는 결코 주장되지 않은 컨트롤의 부재를 설명해요.
Impersonation과 Dag 파일 파싱
run_as_user는 task **실행(execution)**을 다른 Unix 사용자로 한정해요. Dag 파일 import를 한정하지 않아요.
실질적인 run_as_user 값 자체는 Dag 작성자가 제어해요 — Dag이나 task에 설정되어 배포 기본값을 재정의할 수 있어요. 따라서 Airflow는 Dag 파일을 파싱할 때까지 어떤 사용자로 impersonate할지 알 수 없고, Dag 파일 파싱은 모듈 수준 코드를 실행해요. 그 코드는 어떤 impersonation이 일어나기 전에 워커 자신의 Unix 사용자로 실행돼요.
이 순서는 설정이 있는 위치에서 따르는 것이지, 간과에서 따르는 것이 아니며, 설정을 워커가 파싱 전에 읽을 수 있는 곳으로 옮기지 않는 한 뒤집을 수 없어요. Dag 작성자를 한정하기 위해 run_as_user를 사용하는 Deployment Managers는 모듈 수준 Dag 코드가 impersonation 전에 워커 사용자로 실행된다는 보고를 기대 동작으로 읽어야 해요.
run_as_user로 Dag 작성자를 한정하는 Deployment Managers는 그것을 task가 한번 실행되면 무엇을 하는지 제약하는 것으로 읽어야 하지, Dag 파일을 둘러싼 샌드박스로 읽어서는 안 돼요. 모듈 수준 Dag 코드도 한정해야 하는 배포는 그것 안에서 impersonation에 의존하기보다 워커 자체를 격리해야 해요. 워크로드 격리와 현재 한계와 Workload의 impersonation 섹션을 참고하세요.
지원되는 배포 플랫폼
Apache Airflow는 공식적으로 Linux 기반 배포 환경만 지원해요. reference 배포, CI 매트릭스, 공식 Docker 이미지는 모두 Linux 대상(Debian Bookworm)이에요. macOS는 로컬 개발용으로 지원되지만 배포 플랫폼은 아니에요. Windows는 배포용으로 지원되지 않아요 — WSL2를 제외하고요(개발용이며, Linux와 같은 POSIX 파일시스템일 때만).
비 Linux 플랫폼에서만 나타나는 취약점 보고 — Windows 경로 구분자, macOS 특정 파일시스템 의미론에 의존하는 동작 등 — 보안 프로세스에서 범위 밖이에요. 우리는 프로젝트가 지원하지 않는 배포 구성의 플랫폼 특정 버그에 대해 CVE나 권고를 발급하지 않아요.
버그가 지원(Linux)과 미지원(Windows, macOS) 플랫폼 모두에 영향을 주는 보고는 Linux 동작으로 판단돼요; 비 Linux 측면은 정보 제공용이에요.
비 Linux 전용 버그를 식별한 보고자는 여전히 일반 기여 프로세스를 통해 보고해야 해요 — 수정은 CVE나 권고 없이 심층 방어 강화로 환영받아요.
입증된 악용 경로가 없는 강화 기회
많은 보고서는 더 방어적으로 작성될 수 있는 코드를 설명해요 — 문자열 포맷팅으로 조립된 셸 명령, 한정되지 않은 경로, 보고자가 도달 가능함을 보여주지 않은 분기에서 fail open되는 검사. 행위자가 이미 자격이 없던 결과에 도달하는 경로가 없다면, 그 발견은 취약점보다는 강화(hardening)예요.
강화는 공개적으로 처리돼요: 일반 pull request나 이슈를 열면 다른 변경과 마찬가지로 검토돼요. CVE가 할당되지 않고 권고도 발급되지 않아요. 이것은 보안 팀이 가장 자주 적용하는 disposition이며, 퇴짜가 아니에요 — 그러한 findings는 조정된 릴리스를 기다리지 않기 때문이 정확히 더 빠른 경로인 공개 프로세스에 도달한 직후에 종종 수정돼요.
약점을 진술한 다음 그것이 도달 가능하지 않다고 관찰하는 보고는 스스로 질문에 답한 것이에요. 약점 이 도달 가능하다고 믿는다면 경로를 보여주세요; 그것이 어떤 프로세스가 처리할지 결정하는 부분이에요.
전제가 오설정인 Findings
Airflow는 Deployment Managers에게 소프트웨어가 할 일을 넓히는 설정을 줘요: 역직렬화 허용 목록, 이름으로 로드되는 모듈 및 플러그인 경로, test connection 기능 등. "이 허용 목록이 오설정되면 임의 클래스가 인스턴스화될 수 있다" 또는 "오설정된 모듈 경로가 의도하지 않은 코드를 로드할 수 있다"라는 형태의 보고는 안전하지 않은 값을 선택한 결과를 설명해요. 그것을 읽는 코드의 결함을 설명하지 않아요.
그 값을 선택하는 것은 사용자에게 Admin 역할을 부여하는 것과 같은 종류의 Deployment Manager 결정이며, Deployment Managers는 신뢰받아요. 이 형태의 보고가 실행 가능하려면 두 가지 중 하나를 확립해야 해요: 기본 구성이 악용 가능하다는 것, 또는 Deployment Manager가 아닌 어떤 행위자가 설정이 안전하지 않은 값을 취하게 할 수 있다는 것.
제시된 경로 없는 호출 지점에도 같게 적용돼요. import_string(), 역직렬화 헬퍼, 하위 프로세스 호출을 가리키는 것은 공격자 제어 입력이 그것에 도달한다는 것을 확립하지 않아요. 진입점, 입력을 제어하는 행위자의 역할, 그 사이의 각 홉에 이름을 붙이세요 — 그 추적이 발견이지, 호출 지점이 아니에요.
문서화된 설계 속성 다시 말하기
이 문서의 섹션들은 프로젝트가 내린 결정과 그에 따르는 결과를 기록해요: 대칭 JWT 서명 비밀은 토큰을 검증해야 하는 모든 컴포넌트가 보유한다는 것, Dag 작성자가 임의 코드를 실행한다는 것, Execution API가 워크로드를 서로 격리하지 않는다는 것. 증거가 그 섹션 중 하나에 대한 링크인 보고는 프로젝트의 입장을 반박하기보다 다시 말하는 것이며, 같은 재진술을 두 번 이상 제출해도 평가가 바뀌지 않아요.
이런 보고는 이 문서가 설명하는 것과 다른 동작, 또는 문서가 인정하지 않고 Deployment Manager가 합리적으로 예측할 수 없었던 문서화된 결정의 결과를 보여주는 지점에서 유용해져요. 문서화된 결정에 대한 반대는 환영받고 종종 생산적이지만, 보안 보고서라기보다는 개발자 메일링 리스트를 위한 설계 토론이에요.
프로젝트가 릴리스하지 않는 컴포넌트
보안 프로세스는 Apache Airflow 프로젝트가 릴리스하는 것을 다룹니다: apache-airflow, provider 배포판, Task SDK, Helm 차트, reference Docker 이미지. 제3자 플러그인, 프로젝트 밖에서 배포된 operators, 포크, 벤더 빌드, 다른 사람이 만든 이미지는 ASF 릴리스가 아니며 그 밖에 속해요 — 널리 Airflow와 함께 배포되거나 이름에 "airflow"가 들어 있어도요.
그런 것은 그것을 게시한 사람에게 보고하세요. 제3자 컴포넌트가 Airflow 자체가 하는 어떤 일 때문에만 악용 가능하다면, 그 부분은 범위 안에 있고 여기에 보고할 가치가 있어요; 어느 부분이 어느 것인지 말하세요.
전제가 이미 기능을 부여하는 Findings
보고는 공격자에게 이미 할 수 없는 것을 얻어야 해요. 명시된 전제가 메타데이터 데이터베이스에 대한 쓰기 접근, Dag bundle에 파일을 놓을 능력, 또는 문서화된 목적이 시연되는 동작인 권한일 때, 결론은 전제에서 따르며 어떤 경계도 넘지 않았어요.
두 가지 형태가 반복돼요. 첫 번째는 이미 신뢰 경계 안에 있는 principal을 가정해요 — 메타데이터 데이터베이스 작성자가 게이트된 task를 승인할 수 있지만, 그 principal은 이미 task와 Dag run 상태를 직접 설정할 수 있죠. 두 번째는 관리 권한을 에스컬레이션으로 읽어요: FAB 사용자 관리 권한을 가진 역할은 Admin을 포함한 역할을 부여할 수 있는데, 역할 부여가 그 권한의 목적이기 때문이에요.
공격자가 시작 시점에 무엇을 보유하고 끝 시점에 무엇을 보유하는지 진술하세요. 둘이 같으면 보고할 에스컬레이션이 없어요.
secret 마스킹 계약 밖의 값
Secret 마스킹은 Airflow가 secret이라고 told된 값을 다룹니다: connection의 password와 그 extra, 그리고 [core] sensitive_var_conn_names가 확장하는 민감 필드 목록과 일치하는 필드 이름의 값. 로그와 UI에서 우발적 공개를 줄여요. 항상 task에 주어진 값을 읽고 출력할 수 있는 Dag 작성자를 담는 경계는 아니에요.
그 범위의 두 결과는 설계상이에요. 신원 필드는 시크릿이 아니에요 — connection의 login은 민감 필드 목록에 의도적으로 없으며, user와 username도 마찬가지예요 — 그래서 login이 로그에 나타난다는 보고는 의도된 동작을 설명해요. 그리고 Airflow가 민감하다고 told되지 않은 곳(대부분 Dag run 구성)에 놓인 값은 마스킹되지 않아요; 프로젝트는 그들을 마스킹한다고 결코 문서화하지 않았어요.
계약 내에 있고 평문으로 나타나는 값은 실제 버그이며, 대부분의 경우 보안 프로세스보다는 공개 이슈 트래커를 통해 제기할 가치가 있어요.