使用官方 Airflow Docker 镜像集成企业内部 LDAP
解读
在国内金融、运营商、制造等甲方企业里,Airflow 必须接入内部已有的 LDAP(最常见的是 Windows AD 或 OpenLDAP),否则无法通过等保 2.0 的“统一身份鉴别”条款。官方镜像默认使用 Flask-AppBuilder 的 DB 认证,容器一旦重启配置就丢失,所以面试官想确认候选人是否能把“官方镜像 + 企业 LDAP”无缝落地,同时兼顾镜像瘦身、配置可维护、敏感信息不外泄、CI/CD 可回滚四大硬性指标。
知识点
- Airflow 认证链:Webserver → Flask-AppBuilder → AUTH_LDAP;scheduler/worker 不感知 LDAP。
- 官方镜像入口:ENTRYPOINT 为
/entrypoint,会先执行airflow db upgrade,如 LDAP 配置有误会导致容器无限重启。 - 配置优先级:环境变量 > airflow.cfg;但 LDAP 的复杂嵌套结构(group_filter、user_filter)必须落到
webserver_config.py,环境变量无法覆盖。 - 敏感信息:LDAP bindPassword 必须走 Docker Secret(Swarm)或 K8s Secret,绝不能写进镜像层。
- 国产化合规:若甲方要求 SM2/SM3 加密通道,需把 LDAP 的 TLS 证书换成国密双证,并在镜像里预置国密 OpenSSL 动态库。
- 性能调优:group_member_of 属性过大时,需开启 Flask-AppBuilder 的 AUTH_ROLES_SYNC_AT_LOGIN = False,否则每次刷新页面都会全量同步,导致 AD 控制器被打爆。
- 多租户隔离:国内常见“一个 AD 多个 OU”场景,需用
AUTH_USER_REGISTRATION = True+AUTH_USER_REGISTRATION_ROLE = "Viewer",防止未授权用户默认拿到 Op 角色。
答案
步骤一:构建最小化自定义镜像,不改动官方入口脚本,仅追加配置与证书。
FROM apache/airflow:2.8.1-python3.11
USER root
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates ldap-utils && rm -rf /var/lib/apt/lists/*
COPY --chown=airflow:airflow ldap-ca.crt /opt/airflow/certs/ldap-ca.crt
USER airflow
步骤二:在 docker-compose.yml 中把敏感信息做成 Docker Secret(Swarm 场景)或 .env 文件(本地测试)。
secrets:
ldap_bind_password:
external: true
services:
webserver:
image: mycorp/airflow:2.8.1-ldap
secrets:
- ldap_bind_password
environment:
AIRFLOW__WEBSERVER__AUTHENTICATE: "True"
AIRFLOW__WEBSERVER__AUTH_BACKEND: "airflow.www.fab_security.manager.LdapAuthManager"
AIRFLOW__WEBSERVER__CONFIG_FILE: "/opt/airflow/webserver_config.py"
volumes:
- ./webserver_config.py:/opt/airflow/webserver_config.py:ro
步骤三:编写 webserver_config.py,所有 LDAP 参数硬编码在文件里,仅密码读取 /run/secrets/ldap_bind_password,满足等保“密码不落地”要求。
import os
from flask_appbuilder.security.manager import AUTH_LDAP
AUTH_TYPE = AUTH_LDAP
AUTH_LDAP_SERVER = "ldaps://ad.corp.local:636"
AUTH_LDAP_USE_TLS = True
AUTH_LDAP_TLS_CACERTFILE = "/opt/airflow/certs/ldap-ca.crt"
AUTH_LDAP_BIND_USER = "cn=airflow,ou=service,dc=corp,dc=local"
AUTH_LDAP_BIND_PASSWORD = open("/run/secrets/ldap_bind_password").read().strip()
AUTH_LDAP_SEARCH = "ou=users,dc=corp,dc=local"
AUTH_LDAP_UID_FIELD = "sAMAccountName"
AUTH_LDAP_GROUP_FIELD = "memberOf"
AUTH_LDAP_ALLOW_SELF_SIGNED = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "Viewer"
AUTH_ROLES_MAPPING = {
"cn=airflow-admin,ou=groups,dc=corp,dc=local": ["Admin"],
"cn=airflow-op,ou=groups,dc=corp,dc=local": ["Op"],
}
AUTH_ROLES_SYNC_AT_LOGIN = False
步骤四:CI/CD 流水线(GitLab-CI 示例)
build:
stage: build
script:
- docker build -t $CI_REGISTRY/mycorp/airflow:${CI_COMMIT_SHORT_SHA} .
- docker push $CI_REGISTRY/mycorp/airflow:${CI_COMMIT_SHORT_SHA}
deploy:
stage: deploy
script:
- docker stack deploy -c docker-compose.yml airflow --with-registry-auth
only:
- master
关键点:镜像 tag 用 git sha,保证回滚可追踪;docker stack deploy 在国产 ARM 服务器上需加 --resolve-image always,防止镜像摘要不一致。
步骤五:验证
docker exec -it <webserver> airflow users list能看到 LDAP 用户即同步成功。- 故意输错 bindPassword,容器应在 30 秒内退出并输出 “Invalid credentials”,证明错误不会无限重启。
- 用
tcpdump -i eth0 port 636抓包,确认 TLS1.3 国密套件已协商,无明文密码传输。
拓展思考
- 高可用场景:Airflow 2.8 官方 Helm Chart 已支持多 Webserver 副本,需把 webserver_config.py 做成 K8s ConfigMap,并给每个副本挂同一 Secret;此时要防止 Liveness 探针过早触发重启,可把探针初始延迟调到 60 秒,等待 LDAP 首次同步完成。
- 国产替代:若甲方要求替换 OpenLDAP 为 华为云 UCS LDAP 或阿里云云目录,其返回的 memberOf 为字符串数组而非 DN,需重写 AUTH_ROLES_MAPPING 的 value 函数,用
role in [grp.split(',')[0].split('=')[1] for grp in user_groups]提取短名。 - 审计合规:等保 2.0 要求“用户登录日志保存 6 个月”,需在 airflow.cfg 中开启 [logging] remote_logging = True,把 Webserver access log 通过 sidecar 容器实时推到 Kafka → 国密 ELK,并在镜像里预置 国密 TLS 证书自动轮转脚本(update-ca-certificates + inotify)。