스파크 보안
스파크 보안
스파크는 기본적으로 보안 기능이 꺼져 있어요. 인터넷이나 신뢰할 수 없는 네트워크에 열린 클러스터라면, 인가받지 않은 애플리케이션이 돌지 않도록 접근을 막는 것이 매우 중요하죠. 스파크는 여러 배포 유형을 지원하지만 그렇다고 모든 환경에서 안전한 건 아니고, 어떤 것도 '기본적으로 안전'하진 않아요. 이 페이지에서 스파크가 제공하는 보안 기능들 — 인증, 네트워크·로컬 저장 암호화, 웹 UI 보안, 커버로스 등 — 을 하나씩 살펴볼게요.
본문
스파크 RPC 인증
스파크는 RPC 채널에 대해 공유 시크릿을 사용한 인증을 지원해요. spark.authenticate 설정을 켜면 작동하죠. 공유 시크릿을 생성·배포하는 정확한 방식은 배포 유형에 따라 달라요. 아래 명시된 경우가 아니라면 spark.authenticate.secret 설정으로 시크릿을 직접 정의해야 해요. 그 경우 모든 스파크 애플리케이션과 데몬이 같은 시크릿을 공유하게 되는데, 특히 멀티 테넌트 클러스터에서는 보안을 크게 약화시키는 요소가 되어요.
REST 제출 서버는 JWSFilter를 통해 암호학적으로 서명된 JSON Web Token을 담은 HTTP Authorization 헤더를 지원해요. 활성화하려면 스파크 마스터에 spark.master.rest.filters=org.apache.spark.ui.JWSFilter와 spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-KEY를 설정하고, 클라이언트는 공유 시크릿 키로 서명한 JSON Web Token을 포함한 HTTP Authorization 헤더를 제공하면 돼요.
- YARN: Spark on YARN은 공유 시크릿의 생성·배포를 자동으로 처리해요. 애플리케이션마다 고유한 공유 시크릿을 쓰죠. 다만 이 기능은 시크릿 전달이 안전하려면 YARN RPC 암호화가 켜져 있어야 해요. 보안 요구가 높아 시크릿을 DB에 저장하고 싶지 않으면
spark.yarn.shuffle.server.recovery.disabled=true로 설정할 수 있어요(단, 이 경우 External Shuffle Service 재시작 후 셔플 데이터는 복구되지 않아요). - Kubernetes: K8s에서도 애플리케이션마다 고유한 인증 시크릿을 자동 생성해요. 시크릿은 환경 변수로 익스큐터 파드에 전달되는데, 스파크 애플리케이션이 도는 네임스페이스에서 파드를 나열할 수 있는 사용자라면 인증 시크릿도 볼 수 있다는 뜻이에요. 쿠버네티스 관리자가 접근 제어 규칙을 제대로 세워 스파크 인증을 안전하게 유지해야 해요. 대안으로 파일과 쿠버네티스 시크릿을 이용해 인증 시크릿을 파드에 마운트할 수도 있는데(
spark.authenticate.secret.file,spark.authenticate.secret.driver.file,spark.authenticate.secret.executor.file), 파일로 쓸 땐 스파크가 컨테이너에 파일을 마운트해 주지 않으므로 드라이버·익스큐터 시크릿이 일치하도록 직접 안전하게 배포해야 해요.
네트워크 암호화
스파크는 RPC 연결에 대해 상호 배타적인 두 가지 암호화 형태를 지원해요. 선호하는 방법은 Netty의 SSL 지원을 이용한 TLS(SSL) 암호화예요 — 키와 인증서 설정이 필요하고, 표준화되어 있어 더 안전하다고 여겨져요. 레거시 방법은 공유 시크릿에 의존하는 AES 기반 암호화로, RPC 인증이 함께 켜져 있어야 해요. 컴플라이언스상 특정 프로토콜을 써야 하는 경우나 더 표준적인 암호화 라이브러리를 쓰고 싶다면 SSL 기반을, 단순히 전송 중 암호화만 필요하다면 AES 기반이 설정이 간단해서 좋아요. 둘 다 켜져 있으면 SSL 기반이 우선하고 AES 기반은 쓰이지 않아요(경고 메시지가 나와요).
SSL 암호화(권장): RPC 연결에 SSL 암호화를 지원해요. spark.ssl.enabled를 설정했다고 RPC SSL이 자동으로 켜지지는 않아요 — 버전 업그레이드 사용자의 안전한 마이그레이션 경로를 위해 명시적으로 활성화해야 해요.
AES 기반 암호화(레거시): RPC 연결에 AES 암호화를 지원해요. 켜려면 RPC 인증도 함께 켜고 제대로 설정해야 해요. AES는 Apache Commons Crypto 라이브러리를 쓰고, 스파크 설정 시스템이 고급 사용자에게 그 라이브러리 설정에 접근하게 해 줘요. 이 레거시 프로토콜엔 상호 호환되지 않는 두 버전이 있어요. 버전 1은 키 교환 프로토콜 출력에 키 유도 함수(KDF)를 적용하지 않고, 버전 2는 파생된 세션 키가 균일하게 분포하도록 KDF를 적용해요. 기본은 버전 1(하위 호환성)이고, 보안 속성이 더 좋은 버전 2를 권장해요. spark.network.crypto.authEngineVersion으로 1이나 2를 설정할 수 있어요. SASL 기반 암호화도 있지만 deprecated 취급이고, 2.2.0 이전 버전 스파크의 셔플 서비스와 통신할 때만 여전히 필요해요. 주요 설정으로는 spark.network.crypto.enabled(AES 암호화 켜기), spark.network.crypto.cipher(기본 AES/CTR/NoPadding이며 인증되지 않은 방식이라 인증이 되는 AES/GCM/NoPadding 권장), spark.network.crypto.saslFallback(내부 인증 실패 시 SASL로 폴백) 등이 있어요.
로컬 저장 암호화
스파크는 로컬 디스크에 쓰는 임시 데이터 암호화를 지원해요. 셔플 파일, 셔플 스필, 디스크에 저장된 데이터 블록(캐시·브로드캐스트 변수 모두)을 포함하지만, saveAsHadoopFile이나 saveAsTable 같은 API로 생성한 출력 데이터나 사용자가 명시적으로 만든 임시 파일은 포함하지 않아요. spark.io.encryption.enabled=true로 켤 수 있으며, 이 기능을 쓸 땐 RPC 암호화도 함께 켜는 걸 강력히 권장해요.
웹 UI 보안
인증/인가: 모든 UI 포트에 대해 HTTP Authorization 헤더 기반 JWT 인증도 지원해요. spark.ui.filters=org.apache.spark.ui.JWSFilter와 spark.org.apache.spark.ui.JWSFilter.param.secretKey=BASE64URL-ENCODED-KEY로 설정해요.
SSL 설정: 스파크는 SSL 설정을 네임스페이스(${ns})별로 관리해요 — spark.ssl.ui(애플리케이션 웹 UI), spark.ssl.standalone(스탠드얼론 마스터/워커 UI), spark.ssl.historyServer(히스토리 서버 UI), spark.ssl.rpc(RPC 통신). 주요 옵션으로 ${ns}.enabled, ${ns}.port, ${ns}.enabledAlgorithms, ${ns}.keyStore/keyStorePassword/keyStoreType, ${ns}.protocol, ${ns}.needClientAuth, ${ns}.trustStore 등이 있어요. RPC 네임스페이스는 OpenSSL을 쓸 수 있게 ${ns}.openSSLEnabled, ${ns}.privateKey, ${ns}.certChain도 지원해요. 키 스토어·트러스트 스토어 비밀번호는 Hadoop Credential Providers로부터 가져올 수도 있어요.
키 스토어 준비: 키 스토어는 keytool 프로그램으로 생성할 수 있어요(Java 17 참조 문서 제공). 스탠드얼론 배포에서 기본 단계는 노드마다 키 쌍 생성 → 키 쌍의 공개 키를 파일로 내보내기 → 모든 공개 키를 단일 트러스트 스토어에 가져오기 → 트러스트 스토어를 클러스터 노드에 배포예요. YARN 모드에선 --files 인자로 키 스토어를 드라이버와 함께 배포할 수 있고, 스탠드얼론 모드에선 SPARK_MASTER_OPTS/SPARK_WORKER_OPTS/SPARK_DAEMON_JAVA_OPTS 환경 변수로 Java 시스템 속성을 붙여 마스터·워커에 제공해요.
HTTP 보안 헤더: 스파크는 XSS, XFS(크로스 프레임), MIME 스니핑 방지, HSTS 적용을 돕는 HTTP 헤더를 설정할 수 있어요. spark.ui.xXssProtection(기본 1; mode=block), spark.ui.xContentTypeOptions.enabled(기본 true로 nosniff), spark.ui.strictTransportSecurity(SSL/TLS 켜졌을 때만), spark.ui.contentSecurityPolicy.enabled(CSP 헤더, XSS 심층 방어) 등이 있어요.
네트워크 보안용 포트 구성
일반적으로 스파크 클러스터와 그 서비스는 공개 인터넷에 배포되지 않아요. 대부분 사설 서비스로, 조직 네트워크 안에서만 접근 가능해야 해요. 하지만 REST 제출 포트처럼 HTTP Authorization 헤더 기반 JWT 인증도 모든 UI 포트에 지원해요. 스파크가 쓰는 주요 포트는 이래요.
- 브라우저 → 스탠드얼론 마스터: 8080 (웹 UI,
spark.master.ui.port/SPARK_MASTER_WEBUI_PORT) - 브라우저 → 스탠드얼론 워커: 8081 (웹 UI,
spark.worker.ui.port/SPARK_WORKER_WEBUI_PORT) - 드라이버/워커 → 스탠드얼론 마스터: 7077 (잡 제출/클러스터 가입,
SPARK_MASTER_PORT) - 외부 서비스 → 스탠드얼론 마스터: 6066 (REST API로 잡 제출,
spark.master.rest.port)
커버로스
장기 실행 애플리케이션은 접근하려는 서비스에 설정된 위임 토큰(delegation token) 최대 수명을 넘기면 문제가 생길 수 있어요. 이 기능은 모든 곳에서 쓸 수 있는 건 아니고 YARN과 쿠버네티스(client·cluster 모드 모두)에서만 구현돼 있어요. 스파크는 이런 애플리케이션을 위해 새 토큰을 자동 생성할 수 있는데, 두 가지 방법이 있어요.
- 키탭 사용:
spark-submit --principal과--keytab으로 스파크에 principal과 키탭을 주면, 애플리케이션이 위임 토큰을 무기한 가져올 수 있는 유효한 커버로스 로그인을 유지해요. cluster 모드에서 키탭은 HDFS를 스테이징 영역으로 써서 드라이버 머신에 복사되니, YARN과 HDFS를 최소한 암호화로 보호하는 걸 강력히 권장해요. - 티켓 캐시 사용:
spark.kerberos.renewal.credentials=ccache로 설정하면 로컬 커버로스 티켓 캐시를 인증에 써요. 스파크가 재생 가능 수명 동안 티켓을 갱신하지만, 만료 후엔 새 티켓이 필요해요(예:kinit실행). 티켓 캐시 위치는KRB5CCNAME환경 변수로 바꿀 수 있어요.
프록시 사용자: spark-submit --proxy-user로 Hadoop의 Proxy user 기능을 쓸 수 있어요. 대상 클러스터(YARN, HDFS 등)가 시큐어 모드면 임시(proxy-user) 사용자는 커버로스 티켓이 필요 없지만, 슈퍼유저는 유효한 티켓으로 로그인해야 해요. 인증은 대상 클러스터 쪽에서 이뤄지고, 성공하면 클러스터가 proxy 사용자를 대신해 자원 할당과 파일 시스템 접근을 해요. 참고로 배포 모드에 따라 동작이 달라져요 — cluster 모드에선 드라이버를 실행하는 JVM이 proxy 사용자로, client 모드에선 슈퍼유저로 시작돼요.
쿠버네티스와의 안전한 상호작용: 커버로스 뒤의 Hadoop 기반 서비스와 통신할 때 스파크는 위임 토큰을 얻어야 해요. K8s에서 이 토큰은 드라이버와 익스큐터가 공유하는 Secrets에 저장돼요. 커버로스 잡을 제출하는 방식으로는 로컬 티켓 캐시에 TGT를 저장해 kinit으로 제출하거나, 로컬 키탭·principal로 제출하거나, 네임스페이스에 이미 존재하는 위임 토큰 시크릿으로 제출하는 방법 등이 있어요. 모든 경우에 HADOOP_CONF_DIR 또는 spark.kubernetes.hadoop.configMapName을 정의해야 하고, KDC가 컨테이너 안에서 보여야 해요.
이벤트 로깅 보안
이벤트 로깅을 쓰는 애플리케이션이라면 이벤트 로그가 가는 디렉터리(spark.eventLog.dir)를 적절한 권한으로 직접 만들어야 해요. 로그 파일 보안을 위해 디렉터리 권한은 drwxrwxrwxt로 설정하고, 소유자·그룹은 Spark History Server를 실행하는 슈퍼유저와 일치시켜요. 그러면 모든 사용자가 쓰기는 가능하지만, 파일 소유자가 아니면 비특권 사용자가 읽기·삭제·이름 변경을 하지 못하게 막아요. client 모드에서 드라이버 로그도 마찬가지예요 — spark.driver.log.persistToDfs.enabled를 쓰면 spark.driver.log.dfsDir 디렉터리를 같은 방식으로 보호해야 해요.