
클러스터의 문을 열어주는 레플리카셋, 보안은 안녕한가요?
어느 날 갑자기 운영 중인 서비스의 포드가 외부 공격을 받아 클러스터 전체의 권한이 탈취되었다는 긴급 알람을 받는다면 어떤 기분이 들까요? 단순히 포드 하나가 뚫린 것이 아니라, 그 포드를 관리하던 레플리카셋의 설정이 허술해 클러스터 전체로 공격이 퍼져나간 상황이라면 문제는 훨씬 심각해져요.
많은 백엔드 개발자가 레플리카셋을 단순히 포드의 개수를 유지해 주는 도구로만 생각하곤 해요. 하지만 레플리카셋은 포드의 템플릿을 정의하는 핵심 객체이기 때문에, 여기서 정의된 보안 설정이 잘못되면 클러스터 전체의 보안 경계가 무너질 수 있어요. 레플리카셋 보안 설정을 소홀히 하면 공격자는 탈취한 포드를 발판 삼아 다른 네임스페이스로 이동하거나, 노드의 자원을 마음대로 휘두를 수도 있어요.
이제는 단순히 ‘서비스가 잘 돌아가는가’를 넘어 ‘우리 서비스가 안전하게 격리되어 있는가’를 반드시 고민해야 하는 시점이에요. 이 글을 끝까지 읽고 나면, 복잡해 보이는 쿠버네티스 보안을 레플리카셋 수준에서 어떻게 실무적으로 적용할 수 있는지 명확한 기준을 얻게 될 거예요.
오늘 다룰 내용은 다음과 같아요.
- 레플리카셋을 통한 보안 위협 지점 파악하기
- 최소 권한 원칙에 기반한 권한 설계 방법
- 실제 적용 가능한 보안 설정 예제와 실습 가이드
- 정책 도구를 활용한 보안 강제화 전략
- 정기적인 점검과 감사 로그 확인법
보안 설정을 시작하기 전 반드시 알아야 할 핵심 개념
무작정 설정을 바꾸기 전에 우리가 무엇을 보호하려 하는지, 그리고 어떤 도구들을 사용할 수 있는지 아는 것이 우선이에요. 레플리카셋 보안의 핵심은 포드가 가질 수 있는 권한을 최대한 좁히고, 포드가 실행되는 환경을 엄격하게 통제하는 데 있어요.
보안 설정을 위한 3대 핵심 축
쿠버네티스 보안은 크게 세 가지 영역으로 나누어 접근해야 해요. 이 세 가지가 톱니바퀴처럼 맞물려 돌아가야 비로소 단단한 방어벽이 완성돼요.
- RBAC (Role-Based Access Control): 포드가 사용하는 서비스 어카운트가 클러스터 내에서 무엇을 할 수 있는지 결정해요.
- Pod Security Admission (PSA): 포드가 실행될 때 루트 권한을 사용하는지, 호스트의 자원에 접근하는지 등의 규준을 검사해요.
- Network Policy: 포드 간의 통신을 허용할지 차단할지 결정하여 네트워크 침투를 막아요.
레플리카셋 자체에 보안 설정이 들어가는 것이 아니라, 레플리카셋이 생성하는 포드의 podSpec 안에 보안 설정이 포함된다는 점을 꼭 기억하세요. 즉, 레플리카셋의 템플릿을 잘 정의해야 해요.
보안 수준별 설정 기준 비교
현재 우리 서비스의 상황이 어느 단계에 있는지 아래 표를 통해 비교해 보세요. 처음부터 가장 높은 보안 수준을 적용하기 어렵다면, 단계적으로 상향하는 전략이 필요해요.
| 보안 수준 | 주요 특징 | 권장 대상 |
|---|---|---|
| 기본(Privileged) | 모든 권한 허용, 루트 권한 사용 가능 | 로컬 테스트 환경 |
| 중간(Baseline) | 일반적인 권한 제한, 호스트 접근 차단 | 일반적인 스테이징 환경 |
| 높음(Restricted) | 비루트(Non-root) 실행, 읽기 전용 파일 시스템 | 실제 운영 환경(Production) |
대부분의 실무에서는 Restricted 수준을 목표로 하지만, 레거시 애플리케이션의 경우 파일 쓰기 권한 문제로 인해 어려움을 겪기도 해요. 이럴 때는 우선 Baseline 수준을 적용한 뒤, 하나씩 설정을 강화하는 것이 현명한 방법이에요.
실전 레플리카셋 보안 강화 5단계 프로세스
이제 본격적으로 레플리카셋의 템플릿을 어떻게 수정해야 하는지 단계별로 살펴볼게요. 단순히 설정을 넣는 것이 아니라, 각 설정이 어떤 공격을 막아주는지 이해하는 것이 중요해요.
STEP 1. 서비스 어카운트와 RBAC 최소 권한 설계
가장 흔한 실수는 모든 포드가 쿠버네티스의 기본 서비스 어카운트인 default를 사용하는 것이에요. 만약 공격자가 이 포드를 점령했다면, default 어카운트가 가진 권한을 이용해 클러스터의 다른 정보를 훔쳐볼 수 있어요.
먼저, 해당 워크로드를 위한 전용 서비스 어카운트를 만드세요. 그리고 그 어카운트에는 정말로 필요한 권한만 부여된 Role을 연결해야 해요. 예를 들어, 설정값을 읽어오는 포드라면 get, list 권한만 주고, 쓰기 권한은 절대 주지 마세요.
Role은 특정 네임스페이스 내에서만 유효하고, ClusterRole은 클러스터 전체에 영향을 미쳐요. 보안을 위해서는 가급적 네임스페이스 단위의 Role을 사용하세요.
STEP 2. SecurityContext를 통한 런타임 통제
레플리카셋의 spec.template.spec.securityContext 부분을 수정하여 포드의 실행 환경을 엄격하게 제한해야 해요. 여기서 설정하는 값들은 포드가 생성될 때 엔진 수준에서 강제되는 규칙들이에요.
- runAsNonRoot: true: 포드가 루트(root) 사용자로 실행되는 것을 원천 차단해요. 만약 컨테이너 이미지가 루트로 실행되도록 설정되어 있다면 포드 생성 자체가 실패하므로, 개발 단계에서 미리 수정할 수 있어요.
- allowPrivilegeEscalation: false: 자식 프로세스가 부모 프로세스보다 더 많은 권한을 가지는 것을 막아요.
- readOnlyRootFilesystem: true: 컨테이너 내부의 파일 시스템을 읽기 전용으로 만들어요. 공격자가 악성 스크립트를 다운로드하거나 수정하는 것을 방지하는 데 매우 효과적이에요.
애플리케이션이 로그를 남기거나 임시 파일을 생성해야 한다면, emptyDir 볼륨을 마운트하여 특정 경로만 쓰기 가능하게 설정하면 돼요. 이는 보안과 기능성을 모두 잡는 훌륭한 전략이에요.
STEP 3. 네트워크 정책(Network Policy)으로 격리벽 세우기
레플리카셋이 관리하는 포드들이 서로 무분별하게 통신하게 두면 안 돼요. 침입자가 하나의 포드를 장악했을 때 옆에 있는 데이터베이스 포드로 바로 접근할 수 있기 때문이죠.
네트워크 정책을 통해 Ingress(들어오는 통신)와 Egress(나가는 통신)를 명시적으로 정의하세요. 기본적으로 모든 통신을 차단(Deny-all)하는 정책을 먼저 적용한 뒤, 필요한 서비스 간의 통신만 화이트리스트 방식으로 하나씩 허용하는 것이 가장 안전해요.
STEP 4. Admission Controller를 활용한 보안 강제화
개발자가 실수로 보안 설정이 빠진 레플리카셋을 배포하는 것을 막으려면, 클러스터 차원에서 입구 컷을 해야 해요. 이것이 바로 Admission Controller의 역할이에요.
최근에는 Kyverno나 OPA(Open Policy Agent) 같은 도구를 많이 사용해요. 예를 들어, “모든 레플리카셋은 반드시 readOnlyRootFilesystem 설정을 포함해야 한다”라는 정책을 등록해 두면, 이 규칙을 어긴 YAML 파일은 배포 시점에 즉시 거부돼요. 이는 사람이 하는 실수를 시스템이 자동으로 잡아주는 아주 강력한 방법이에요.
STEP 5. 리소스 제한(Resource Quotas)을 통한 DoS 방어
보안은 단순히 권한 탈취뿐만 아니라 가용성을 지키는 것도 포함해요. 특정 포드가 무한 루프에 빠지거나 해킹을 당해 채굴 프로그램이 돌아가면, 노드의 CPU와 메모리를 모두 점유하여 다른 서비스까지 죽게 만들 수 있어요.
레플리카셋의 템플릿에 반드시 resources.limits와 resources.requests를 설정하세요. 이를 통해 포드가 사용할 수 있는 자원의 최대치를 정해두면, 하나의 포드가 클러스터 전체를 마비시키는 것을 방지할 수 있어요.
리소스 제한을 너무 타이트하게 잡으면 애플리케이션이 갑작스러운 트래픽 증가 시 OOMKilled(메모리 부족으로 인한 종료)를 당할 수 있어요. 반드시 충분한 테스트를 거쳐 적절한 수치를 찾아야 해요.
보안 설정 적용 시나리오 예시
아래는 보안이 강화된 레플리카셋의 YAML 구조를 추상화한 예시예요. 실제 적용 시에는 이 구조를 참고하여 본인의 환경에 맞게 수정해 보세요.
먼저, 서비스 어카운트를 지정하고, securityContext를 통해 실행 권한을 제한하며, resources로 자원을 통제하는 흐름을 따라야 해요.
1. apiVersion: apps/v1
2. kind: ReplicaSet
3. spec.template.spec.serviceAccountName: my-app-sa
4. spec.template.spec.securityContext.runAsNonRoot: true
5. spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem: true
6. spec.template.spec.containers[0].resources.limits.cpu: "500m"
자주 하는 실수와 해결법
실무에서 보안 설정을 적용하다 보면 예상치 못한 오류 때문에 머리를 싸매는 경우가 많아요. 가장 자주 발생하는 5가지 사례와 그 해결책을 정리했어요.
- ❌ 실수: 기본(default) 서비스 어카운트에 과도한 권한 부여
왜 발생하는가: 설정을 귀찮아하여 모든 포드가 모든 것을 할 수 있는 기본 어카운트를 공유하기 때문이에요.
✅ 해결법: 워크로드별로 전용 ServiceAccount를 생성하고, 최소한의 권한만 담긴 Role을 연결하세요. - ❌ 실수: 컨테이너를 루트(root) 사용자로 실행
왜 발생하는가: 컨테이너 이미지를 만들 때 별도의 사용자 설정을 하지 않으면 기본적으로 루트로 실행돼요.
✅ 해결법: Dockerfile 작성 시USER명령어를 사용하여 비루트 사용자를 명시하고, 레플리카셋 템플릿에서도runAsNonRoot: true를 설정하세요. - ❌ 실수: 파일 시스템을 쓰기 가능(Writable) 상태로 방치
왜 발생하는가: 애플리케이션이 로그를 파일로 남기거나 임시 파일을 만들어야 한다는 이유로 편의를 위해 허용하기 때문이에요.
✅ 해결법:readOnlyRootFilesystem: true를 적용하되, 쓰기가 필요한 경로는emptyDir볼륨을 마운트하여 격리된 공간을 제공하세요. - ❌ 실수: 네트워크 정책 없이 모든 포드 간 통신 허용
왜 발생하는가: 네트워크 설정이 복잡해질 것을 우려하여 기본 설정(모든 통신 허용)을 그대로 두기 때문이에요.
✅ 해결법: 네임스페이스 단위의 Default Deny 정책을 먼저 적용한 뒤, 필요한 통신 경로만NetworkPolicy로 하나씩 허용하세요. - ❌ 실수: 리소스 제한(Limit) 설정 누락
왜 발생하는가: 자원 계산이 어렵고, 설정이 없으면 포드가 자유롭게 사용할 수 있다는 안일한 생각 때문이에요.
✅ 해결법: 반드시limits와requests를 설정하여 자원 고갈로 인한 서비스 마비(DoS)를 방지하세요.
자주 묻는 질문
Q. 보안 설정을 강화하면 애플리케이션 성능이 떨어지나요?
보안 설정 자체가 CPU나 메모리를 직접적으로 소모하지는 않아요. 오히려 리소스 제한을 통해 자원을 효율적으로 관리하게 되므로, 성능 저하보다는 시스템 전체의 안정성이 높아지는 이득이 훨씬 커요.
Q. Pod Security Admission(PSA)와 OPA/Kyverno 중 무엇을 써야 할까요?
PSA는 쿠버네티스에 내장되어 있어 설정이 매우 간편하지만, 규칙이 다소 단순해요. 만약 “특정 라벨이 있는 포드만 특정 설정을 허용한다”와 같이 매우 세밀하고 복잡한 정책이 필요하다면 Kyverno나 OPA를 사용하는 것을 추천해요.
Q. 이미 실행 중인 레플리카셋의 보안 설정을 바꾸면 어떻게 되나요?
레플리카셋의 템플릿을 수정하면 쿠버네티스는 기존 포드를 삭제하고 새로운 설정이 적용된 포드를 다시 생성해요. 따라서 서비스 중단이 발생할 수 있으니, 반드시 Rolling Update 전략을 확인하고 적용하세요.
Q. 네트워크 정책을 적용했는데 포드 간 통신이 안 돼요. 무엇을 확인해야 할까요?
먼저 레이블(Label)이 정확한지 확인하세요. NetworkPolicy는 포드의 IP가 아니라 레이블을 기준으로 동작하거든요. 그다음, Egress 규칙이 나가는 통신까지 허용하고 있는지 체크해 보세요.
안전한 운영을 위한 마지막 체크포인트
레플리카셋 보안은 한 번의 설정으로 끝나는 이벤트가 아니라, 지속적인 관리가 필요한 프로세스예요. 오늘 배운 내용을 바탕으로 지금 바로 여러분의 클러스터를 점검해 보세요.
- 전용 ServiceAccount를 만들고 권한을 최소화했나요?
runAsNonRoot: true설정을 적용했나요?readOnlyRootFilesystem: true로 파일 시스템을 잠갔나요?- NetworkPolicy로 불필요한 포드 간 통신을 차단했나요?
resources.limits를 설정하여 자원 독점을 막았나요?- Admission Controller를 통해 보안 규칙을 강제하고 있나요?
보안은 완벽할 수 없지만, 공격자가 침투하기 어렵게 만드는 문턱을 높이는 것은 가능해요. 그 문턱을 만드는 첫걸음이 바로 오늘 다룬 레플리카셋 보안 설정이에요.
지금 바로 시작할 일
- 오늘 할 일: 운영 중인 주요 서비스의 레플리카셋 템플릿에서
runAsNonRoot설정이 되어 있는지 확인하기 - 이번 주 할 일: 서비스별로 전용 ServiceAccount를 생성하고 RBAC 권한 최적화하기
- 실행 직전 할 일: 테스트 환경에서 NetworkPolicy를 적용해 보고 애플리케이션 통신에 문제가 없는지 검증하기
실습 환경에서 직접 설정을 적용해 보다가 막히는 부분이 있다면 언제든 댓글로 질문을 남겨 주세요. 함께 고민하며 더 안전한 서비스를 만들어 가요!
함께 읽으면 좋은 글: 쿠버네티스 레플리카셋 기본 개념 완벽 정리, 안전한 클러스터 구축을 위한 입문 가이드