본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 27. · 2 Views
클라우드 보안 실전 완벽 가이드
AWS, Docker, Kubernetes 환경에서 보안을 자동화하고 취약점을 사전에 탐지하는 방법을 알아봅니다. 초급 개발자도 바로 적용할 수 있는 실전 보안 점검 스크립트와 CI/CD 파이프라인 보안 구축 방법을 다룹니다.
목차
1. AWS 보안 점검 자동화
어느 날 김개발 씨는 AWS 콘솔에 접속했다가 깜짝 놀랐습니다. 분명히 테스트용으로 만들어둔 EC2 인스턴스가 누군가에 의해 암호화폐 채굴에 사용되고 있었던 것입니다.
"도대체 어디서부터 잘못된 걸까요?" 보안팀장 박시니어 씨가 한숨을 쉬며 대답했습니다. "우리가 보안 점검을 수동으로 하다 보니 놓친 거예요.
이제 자동화가 필요합니다."
AWS 보안 점검 자동화란 boto3 라이브러리를 활용하여 AWS 리소스의 보안 상태를 프로그래밍 방식으로 검사하는 것입니다. 마치 건물 관리인이 매일 밤 모든 문과 창문이 잠겼는지 순찰하는 것처럼, 자동화된 스크립트가 24시간 우리의 클라우드 인프라를 감시합니다.
이를 통해 사람의 실수로 놓칠 수 있는 보안 취약점을 빠르게 발견할 수 있습니다.
다음 코드를 살펴봅시다.
import boto3
from datetime import datetime, timezone
# AWS 보안 점검 자동화 스크립트
def check_security_groups():
ec2 = boto3.client('ec2')
findings = []
# 모든 보안 그룹 조회
security_groups = ec2.describe_security_groups()['SecurityGroups']
for sg in security_groups:
for rule in sg.get('IpPermissions', []):
# 0.0.0.0/0 으로 모든 IP에 개방된 규칙 탐지
for ip_range in rule.get('IpRanges', []):
if ip_range.get('CidrIp') == '0.0.0.0/0':
findings.append({
'group_id': sg['GroupId'],
'group_name': sg['GroupName'],
'port': rule.get('FromPort', 'All'),
'risk': 'HIGH - 전체 인터넷에 개방됨'
})
return findings
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 회사에서 AWS를 사용하기 시작한 지 얼마 되지 않았는데, 벌써 보안 사고가 터진 것입니다.
다행히 큰 피해는 없었지만, 이번 일을 계기로 보안 점검 자동화의 필요성을 절실히 느꼈습니다. 박시니어 씨가 화이트보드 앞에 섰습니다.
"자, 오늘부터 우리는 boto3라는 도구를 사용할 겁니다. 이게 뭐냐면, Python에서 AWS 서비스를 제어할 수 있게 해주는 공식 라이브러리예요." 그렇다면 보안 점검 자동화란 정확히 무엇일까요?
쉽게 비유하자면, 이것은 마치 아파트 경비원이 CCTV 화면을 24시간 모니터링하는 것과 같습니다. 사람이 직접 모든 층을 돌아다니며 확인하는 것보다 훨씬 효율적이고, 놓치는 것도 적습니다.
보안 점검 자동화도 마찬가지로, 스크립트가 주기적으로 모든 AWS 리소스를 검사하여 위험한 설정을 찾아냅니다. 자동화가 없던 시절에는 어땠을까요?
개발자들은 AWS 콘솔에 직접 로그인해서 보안 그룹을 하나하나 클릭해가며 확인해야 했습니다. 리소스가 10개일 때는 괜찮았지만, 100개, 1000개가 되면 사실상 불가능해집니다.
더 큰 문제는 사람은 피곤하면 실수한다는 것입니다. 금요일 저녁, 빨리 퇴근하고 싶은 마음에 대충 확인하고 넘어갔다가 주말 사이에 해커가 침입하는 일이 실제로 많이 발생합니다.
바로 이런 문제를 해결하기 위해 보안 점검 자동화가 등장했습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 boto3.client('ec2')로 EC2 서비스에 연결합니다. 이것이 AWS와 우리 스크립트를 연결하는 다리 역할을 합니다.
다음으로 describe_security_groups()를 호출하여 모든 보안 그룹 목록을 가져옵니다. 핵심은 0.0.0.0/0 이라는 CIDR 블록을 찾는 부분입니다.
이 표기법은 "인터넷의 모든 IP 주소"를 의미합니다. 즉, 전 세계 누구나 해당 포트에 접근할 수 있다는 뜻입니다.
SSH 포트인 22번이나 데이터베이스 포트인 3306번이 이렇게 열려 있다면, 그것은 마치 집 현관문을 활짝 열어두고 외출하는 것과 같습니다. 실제 현업에서는 이 스크립트를 AWS Lambda와 연동하여 매일 자동으로 실행되게 설정합니다.
위험한 설정이 발견되면 Slack이나 이메일로 알림을 보내도록 구성하는 것이 일반적입니다. 많은 기업에서 이런 방식으로 보안 감시 체계를 구축하고 있습니다.
하지만 주의할 점도 있습니다. 모든 0.0.0.0/0 설정이 잘못된 것은 아닙니다.
웹 서버의 80번, 443번 포트는 당연히 전체에 개방되어야 합니다. 따라서 포트별로 다른 기준을 적용해야 합니다.
22번(SSH), 3389번(RDP), 3306번(MySQL) 같은 관리용 포트만 위험으로 분류하는 것이 올바른 접근입니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 해커가 들어올 수 있었군요.
SSH 포트가 전체에 열려 있었네요!" 이제 김개발 씨는 이 스크립트를 매일 새벽 3시에 실행되도록 설정했습니다. 덕분에 누군가 실수로 위험한 보안 그룹을 만들어도 다음 날 아침에 바로 알 수 있게 되었습니다.
실전 팁
💡 - boto3 사용 전에 aws configure로 자격 증명을 설정하거나 IAM Role을 활용하세요
- 발견된 취약점은 SNS 토픽을 통해 Slack이나 이메일로 즉시 알림을 보내도록 구성하세요
- CloudWatch Events를 활용하면 정기적인 스케줄 실행이 가능합니다
2. S3 버킷 보안 점검
김개발 씨가 S3 버킷을 생성한 지 일주일이 지났습니다. 그런데 어느 날 보안팀에서 긴급 연락이 왔습니다.
"개발팀에서 만든 버킷 중에 퍼블릭으로 열린 게 있어요. 혹시 고객 데이터 들어있는 건 아니죠?" 식은땀이 흘렀습니다.
다행히 테스트 데이터만 있었지만, 만약 실제 고객 정보였다면 큰일 날 뻔했습니다.
S3 버킷 보안 점검은 클라우드 스토리지의 접근 권한을 검사하여 의도치 않은 데이터 노출을 방지하는 것입니다. 마치 금고의 잠금 상태를 확인하는 것처럼, S3 버킷이 외부에 열려 있지는 않은지, 암호화는 적용되어 있는지 자동으로 확인합니다.
이를 통해 데이터 유출 사고를 사전에 예방할 수 있습니다.
다음 코드를 살펴봅시다.
import boto3
def check_s3_security():
s3 = boto3.client('s3')
findings = []
# 모든 버킷 조회
buckets = s3.list_buckets()['Buckets']
for bucket in buckets:
bucket_name = bucket['Name']
issues = []
# 퍼블릭 액세스 차단 설정 확인
try:
public_access = s3.get_public_access_block(Bucket=bucket_name)
config = public_access['PublicAccessBlockConfiguration']
if not all([config.get('BlockPublicAcls'),
config.get('BlockPublicPolicy')]):
issues.append('퍼블릭 액세스 차단 미설정')
except:
issues.append('퍼블릭 액세스 차단 설정 없음')
# 버킷 암호화 확인
try:
s3.get_bucket_encryption(Bucket=bucket_name)
except:
issues.append('서버 측 암호화 미적용')
if issues:
findings.append({'bucket': bucket_name, 'issues': issues})
return findings
S3 버킷 보안 사고는 클라우드 보안 사고 중 가장 흔한 유형입니다. 실제로 많은 대기업에서 S3 버킷 설정 실수로 인해 수백만 건의 고객 데이터가 유출된 사례가 있습니다.
왜 이런 일이 반복될까요? 문제는 S3의 기본 설정이 과거에는 상당히 관대했다는 점입니다.
개발 편의를 위해 "일단 퍼블릭으로 열어두자"라고 생각하고 만든 버킷이 그대로 방치되는 경우가 많았습니다. 박시니어 씨가 설명했습니다.
"S3 버킷은 마치 클라우드에 있는 USB 드라이브 같아요. 아무나 접근하게 두면 안 되겠죠?" 퍼블릭 액세스 차단이란 무엇일까요?
이것은 S3 버킷에 대한 일종의 안전장치입니다. 마치 어린이 안전 캡처럼, 실수로 버킷을 공개 설정하더라도 이 차단 설정이 켜져 있으면 실제로 공개되지 않습니다.
AWS에서는 이 설정을 계정 수준과 버킷 수준 모두에서 적용할 것을 강력히 권장합니다. 위 코드에서 get_public_access_block()는 이 안전장치가 제대로 설정되어 있는지 확인합니다.
BlockPublicAcls와 BlockPublicPolicy가 모두 True여야 안전합니다. 하나라도 False이거나 설정 자체가 없다면 위험 신호입니다.
서버 측 암호화도 중요한 점검 항목입니다. 암호화는 마치 금고 안의 서류를 다시 한번 봉투에 넣어 밀봉하는 것과 같습니다.
만약 누군가 금고에 침입하더라도, 밀봉된 봉투를 열지 못하면 내용을 볼 수 없습니다. S3의 서버 측 암호화도 마찬가지로, 데이터가 저장될 때 자동으로 암호화되어 추가적인 보안 계층을 제공합니다.
코드에서 get_bucket_encryption()을 호출했을 때 예외가 발생하면, 해당 버킷에 암호화가 설정되지 않은 것입니다. 이런 버킷은 즉시 암호화를 적용해야 합니다.
실제 현업에서는 이 점검을 AWS Config Rules와 연동하여 실시간 모니터링을 구현합니다. 새로운 버킷이 생성될 때마다 자동으로 규정 준수 여부를 확인하고, 문제가 있으면 즉시 알림을 보내거나 자동으로 수정하도록 설정할 수 있습니다.
주의할 점은 정적 웹 호스팅용 버킷은 예외라는 것입니다. 웹사이트를 호스팅하려면 당연히 퍼블릭 접근이 필요합니다.
따라서 버킷의 용도를 태그로 명확히 구분하고, 웹 호스팅 버킷은 점검 대상에서 제외하는 로직을 추가하는 것이 좋습니다. 김개발 씨는 이 스크립트를 실행해본 후 놀랐습니다.
자신이 만든 버킷 외에도 다른 팀에서 만든 퍼블릭 버킷이 5개나 더 있었던 것입니다. 모두에게 이 스크립트를 공유하고, 정기 점검 체계를 구축하기로 했습니다.
실전 팁
💡 - S3 버킷 이름에 환경(dev, prod)과 용도를 명확히 표기하여 관리하세요
- AWS Config의 s3-bucket-public-read-prohibited 규칙을 활성화하면 실시간 감시가 가능합니다
- 버킷 정책은 최소 권한 원칙을 적용하여 필요한 IAM 역할에만 접근을 허용하세요
3. IAM 권한 최소화
"이 IAM 사용자한테 왜 AdministratorAccess가 붙어있어요?" 보안 감사에서 지적을 받은 김개발 씨는 얼굴이 빨개졌습니다. "아, 그게...
처음에 권한 설정하기 복잡해서 일단 관리자 권한을 줬는데..." 박시니어 씨가 한숨을 쉬었습니다. "그게 바로 보안 사고의 시작이에요.
오늘은 IAM 권한 최소화에 대해 배워봅시다."
IAM 권한 최소화란 사용자와 서비스에게 업무 수행에 꼭 필요한 최소한의 권한만 부여하는 원칙입니다. 마치 회사에서 모든 직원에게 금고 열쇠를 주지 않고, 경리 담당자에게만 주는 것과 같습니다.
이 원칙을 지키면 계정이 탈취되더라도 피해를 최소화할 수 있습니다.
다음 코드를 살펴봅시다.
import boto3
from datetime import datetime, timezone, timedelta
def audit_iam_permissions():
iam = boto3.client('iam')
findings = []
# 모든 사용자 조회
users = iam.list_users()['Users']
for user in users:
username = user['UserName']
user_issues = []
# 연결된 정책 확인
policies = iam.list_attached_user_policies(UserName=username)
for policy in policies['AttachedPolicies']:
# 관리자 권한 탐지
if 'AdministratorAccess' in policy['PolicyArn']:
user_issues.append('과도한 관리자 권한 보유')
# 마지막 활동 시간 확인 (90일 이상 미사용)
try:
last_used = iam.get_user(UserName=username)['User']
if 'PasswordLastUsed' in last_used:
last_login = last_used['PasswordLastUsed']
if datetime.now(timezone.utc) - last_login > timedelta(days=90):
user_issues.append('90일 이상 미사용 계정')
except:
pass
if user_issues:
findings.append({'user': username, 'issues': user_issues})
return findings
**최소 권한 원칙(Principle of Least Privilege)**은 보안의 가장 기본적인 원칙 중 하나입니다. 그런데 왜 많은 개발자들이 이 원칙을 무시하고 관리자 권한을 남발할까요?
박시니어 씨가 설명했습니다. "솔직히 말하면, 권한 설정이 귀찮거든요.
처음에 딱 맞는 권한을 찾아서 설정하려면 시간이 걸려요. 그래서 '일단 관리자 권한 주고, 나중에 줄이자'라고 생각하는데...
그 '나중에'는 절대 오지 않아요." 이것은 마치 모든 직원에게 회사 금고, 서버실, 사장실 열쇠를 다 주는 것과 같습니다. 만약 한 명의 열쇠가 도둑맞으면 어떻게 될까요?
회사 전체가 위험해집니다. 하지만 경리에게는 금고 열쇠만, IT 담당자에게는 서버실 열쇠만 주면, 한 명의 열쇠가 도둑맞아도 피해를 최소화할 수 있습니다.
위 코드는 두 가지를 점검합니다. 첫째, AdministratorAccess 정책이 연결된 사용자를 찾습니다.
이 정책은 AWS의 모든 서비스에 대한 완전한 접근 권한을 부여합니다. 실제로 이 권한이 필요한 사람은 극소수입니다.
둘째, 90일 이상 미사용 계정을 찾습니다. 퇴사한 직원의 계정, 더 이상 사용하지 않는 서비스 계정 등이 방치되어 있으면 해커의 표적이 됩니다.
오래된 계정은 비활성화하거나 삭제해야 합니다. list_attached_user_policies()는 사용자에게 직접 연결된 관리형 정책을 조회합니다.
여기서 중요한 것은 인라인 정책과 그룹을 통해 상속받은 권한도 별도로 확인해야 한다는 점입니다. 완벽한 감사를 위해서는 list_user_policies()와 list_groups_for_user()도 함께 사용해야 합니다.
실무에서 권한 최소화를 실천하는 방법은 무엇일까요? 먼저 AWS Access Analyzer를 활용할 수 있습니다.
이 도구는 실제 사용 패턴을 분석하여 필요한 최소 권한을 추천해줍니다. 관리자 권한으로 시작했더라도, 30일 정도 모니터링한 후 실제 사용한 권한만 남기고 나머지를 제거할 수 있습니다.
또한 **IAM 역할(Role)**을 적극 활용해야 합니다. 장기 자격 증명인 액세스 키 대신, 임시 자격 증명을 발급받는 역할을 사용하면 키 유출 위험을 줄일 수 있습니다.
김개발 씨는 감사 스크립트를 돌린 후 충격을 받았습니다. 회사에 AdministratorAccess를 가진 사용자가 15명이나 있었고, 그중 5명은 이미 퇴사한 직원이었습니다.
즉시 정리 작업에 들어갔습니다.
실전 팁
💡 - 새 사용자 생성 시 PowerUserAccess 대신 필요한 서비스별 권한만 부여하세요
- AWS Access Analyzer를 활용하여 사용하지 않는 권한을 주기적으로 정리하세요
- MFA(다중 인증)를 모든 IAM 사용자에게 필수로 적용하세요
4. Docker 컨테이너 보안 스캔
"이 Docker 이미지 언제 만든 거예요?" 박시니어 씨가 물었습니다. "음...
한 6개월 전쯤이요. 왜요?" 김개발 씨가 대답했습니다.
"그때 이후로 베이스 이미지에 심각한 보안 취약점이 10개나 발견됐어요. 우리 프로덕션에 이 이미지가 돌아가고 있는 거죠?" 김개발 씨의 얼굴이 하얗게 질렸습니다.
Docker 컨테이너 보안 스캔은 컨테이너 이미지에 포함된 취약점을 자동으로 탐지하는 과정입니다. 마치 공항 보안 검색대처럼, 이미지가 배포되기 전에 위험한 요소가 없는지 검사합니다.
Trivy 같은 도구를 활용하면 알려진 CVE(공통 취약점 및 노출) 목록과 대조하여 위험을 사전에 발견할 수 있습니다.
다음 코드를 살펴봅시다.
import subprocess
import json
def scan_docker_image(image_name):
"""Trivy를 사용한 Docker 이미지 보안 스캔"""
# Trivy로 이미지 스캔 (JSON 출력)
result = subprocess.run(
['trivy', 'image', '--format', 'json',
'--severity', 'HIGH,CRITICAL', image_name],
capture_output=True, text=True
)
scan_result = json.loads(result.stdout)
vulnerabilities = []
# 취약점 분석
for target in scan_result.get('Results', []):
for vuln in target.get('Vulnerabilities', []):
vulnerabilities.append({
'id': vuln['VulnerabilityID'],
'package': vuln['PkgName'],
'severity': vuln['Severity'],
'installed': vuln['InstalledVersion'],
'fixed': vuln.get('FixedVersion', '수정 버전 없음'),
'title': vuln.get('Title', '설명 없음')
})
return {
'image': image_name,
'total_vulnerabilities': len(vulnerabilities),
'critical': len([v for v in vulnerabilities if v['severity'] == 'CRITICAL']),
'high': len([v for v in vulnerabilities if v['severity'] == 'HIGH']),
'details': vulnerabilities[:10] # 상위 10개만 반환
}
컨테이너 보안은 왜 중요할까요? Docker 이미지는 여러 레이어로 구성되어 있습니다.
베이스 이미지부터 시작해서 각종 패키지, 라이브러리, 그리고 우리의 애플리케이션 코드까지 층층이 쌓여 있습니다. 문제는 이 레이어들 중 어느 하나라도 취약점이 있으면 전체 컨테이너가 위험해진다는 것입니다.
박시니어 씨가 비유를 들었습니다. "Docker 이미지는 마치 샌드위치 같아요.
빵, 상추, 토마토, 고기가 층층이 쌓여 있죠. 그런데 만약 상추가 상했다면?
전체 샌드위치를 못 먹게 되는 거예요. 컨테이너도 마찬가지로, 베이스 이미지에 취약점이 있으면 우리 코드가 아무리 안전해도 소용없어요." Trivy는 Aqua Security에서 만든 오픈소스 취약점 스캐너입니다.
사용이 간편하고 스캔 속도가 빠르며, 다양한 취약점 데이터베이스를 활용합니다. 위 코드에서 --severity HIGH,CRITICAL 옵션은 심각도가 높은 취약점만 필터링합니다.
**CVE(Common Vulnerabilities and Exposures)**는 공개적으로 알려진 보안 취약점에 부여되는 고유 식별자입니다. 예를 들어 CVE-2021-44228은 그 유명한 Log4j 취약점입니다.
Trivy는 이런 CVE 데이터베이스와 이미지를 대조하여 취약한 패키지를 찾아냅니다. 코드를 살펴보면, subprocess.run()으로 Trivy CLI를 실행하고 JSON 형식으로 결과를 받습니다.
결과에서 각 취약점의 ID, 패키지명, 심각도, 설치된 버전, 수정 버전을 추출합니다. 특히 FixedVersion이 있다면 해당 버전으로 업그레이드하면 취약점을 해결할 수 있습니다.
실무에서는 이 스캔을 CI/CD 파이프라인에 통합합니다. 이미지 빌드 후 자동으로 스캔하고, CRITICAL 취약점이 발견되면 빌드를 실패시킵니다.
이렇게 하면 취약한 이미지가 프로덕션에 배포되는 것을 원천 차단할 수 있습니다. 주의할 점은 모든 취약점을 즉시 수정할 수는 없다는 것입니다.
때로는 수정 버전이 아직 나오지 않았거나, 업그레이드가 호환성 문제를 일으킬 수 있습니다. 이런 경우에는 해당 취약점의 실제 영향도를 분석하고, 다른 보안 조치(네트워크 격리, WAF 등)로 위험을 완화해야 합니다.
김개발 씨는 프로덕션에서 사용 중인 모든 이미지를 스캔했습니다. 결과는 충격적이었습니다.
총 47개의 CRITICAL 취약점과 128개의 HIGH 취약점이 발견되었습니다. 주말 내내 이미지 업데이트 작업을 해야 했지만, 그래도 사고가 나기 전에 발견해서 다행이었습니다.
실전 팁
💡 - 베이스 이미지는 Alpine이나 Distroless처럼 경량화된 것을 사용하면 공격 표면을 줄일 수 있습니다
- 이미지 빌드 시 latest 태그 대신 특정 버전을 명시하여 예측 가능성을 확보하세요
- GitHub Actions나 GitLab CI에 Trivy를 통합하면 PR마다 자동 스캔이 가능합니다
5. Kubernetes 보안
"쿠버네티스 클러스터가 해킹당했어요!" 새벽 3시에 온 콜이 울렸습니다. 김개발 씨는 잠에서 깨어 급히 노트북을 열었습니다.
조사 결과, 누군가 권한이 과도하게 설정된 Pod를 통해 클러스터 전체에 접근한 것으로 밝혀졌습니다. "Pod에 왜 cluster-admin 권한이 있는 거죠?" 박시니어 씨의 질문에 아무도 대답하지 못했습니다.
Kubernetes 보안은 컨테이너 오케스트레이션 환경에서 워크로드와 클러스터를 보호하는 것입니다. 마치 아파트 단지의 경비 시스템처럼, 누가 어디에 접근할 수 있는지, 각 세대(Pod)가 어떤 권한을 가지는지 철저히 관리해야 합니다.
RBAC, Pod Security Standards, 네트워크 정책 등을 활용하여 다층 방어를 구축합니다.
다음 코드를 살펴봅시다.
import subprocess
import json
import yaml
def audit_kubernetes_security():
"""Kubernetes 클러스터 보안 감사"""
findings = []
# 권한이 과도한 Pod 탐지 (privileged 모드)
pods_json = subprocess.run(
['kubectl', 'get', 'pods', '-A', '-o', 'json'],
capture_output=True, text=True
)
pods = json.loads(pods_json.stdout)
for pod in pods.get('items', []):
pod_name = pod['metadata']['name']
namespace = pod['metadata']['namespace']
for container in pod['spec'].get('containers', []):
security_context = container.get('securityContext', {})
# 위험한 설정 탐지
if security_context.get('privileged', False):
findings.append({
'type': 'PRIVILEGED_CONTAINER',
'severity': 'CRITICAL',
'pod': pod_name,
'namespace': namespace,
'message': 'privileged 모드로 실행 중 - 호스트 전체 접근 가능'
})
if security_context.get('runAsUser') == 0:
findings.append({
'type': 'ROOT_USER',
'severity': 'HIGH',
'pod': pod_name,
'namespace': namespace,
'message': 'root 사용자로 실행 중'
})
return findings
Kubernetes는 강력한 만큼 복잡합니다. 그리고 복잡함은 종종 보안 구멍으로 이어집니다.
특히 초보자들이 자주 하는 실수 중 하나가 privileged 모드를 무분별하게 사용하는 것입니다. privileged 모드란 무엇일까요?
박시니어 씨가 설명했습니다. "일반적으로 컨테이너는 호스트 시스템과 격리되어 있어요.
마치 아파트의 각 세대가 분리되어 있는 것처럼요. 그런데 privileged 모드를 켜면, 그 컨테이너는 아파트 관리실 마스터키를 갖게 되는 거예요.
모든 세대, 모든 시설에 접근할 수 있죠." 이것이 왜 위험할까요? 만약 해커가 privileged 컨테이너 안으로 침입하면, 컨테이너 격리를 벗어나 호스트 노드 전체를 장악할 수 있습니다.
그리고 노드를 장악하면 클러스터의 다른 Pod들도 공격할 수 있습니다. 위 코드에서는 kubectl get pods로 모든 Pod 정보를 가져온 후, 각 컨테이너의 securityContext를 검사합니다.
privileged: true이거나 runAsUser: 0(root)인 경우 위험으로 분류합니다. **RBAC(Role-Based Access Control)**도 중요한 검사 항목입니다.
코드에는 포함되지 않았지만, kubectl get clusterrolebindings를 통해 cluster-admin 권한이 부여된 서비스 계정을 찾아내야 합니다. 대부분의 애플리케이션은 cluster-admin이 필요 없습니다.
네트워크 정책도 확인해야 합니다. 기본적으로 Kubernetes의 모든 Pod는 서로 통신할 수 있습니다.
이것은 마치 아파트 단지 내 모든 세대 문이 항상 열려 있는 것과 같습니다. NetworkPolicy를 사용하여 Pod 간 통신을 제한해야 합니다.
실무에서는 OPA Gatekeeper나 Kyverno 같은 정책 엔진을 사용합니다. 이 도구들은 클러스터에 적용되는 모든 리소스를 검사하여, 보안 정책을 위반하는 설정은 아예 배포되지 못하게 차단합니다.
예를 들어 "privileged 컨테이너 금지" 정책을 설정하면, 누군가 실수로 privileged: true를 넣어도 배포가 거부됩니다. 김개발 씨는 감사 스크립트를 돌려본 후 긴 리스트를 받았습니다.
놀랍게도 개발팀이 "편의상" 설정해둔 privileged Pod가 12개나 있었습니다. 모두 일반 권한으로도 충분히 동작할 수 있는 것들이었습니다.
하나씩 수정 작업에 들어갔습니다.
실전 팁
💡 - Pod Security Standards(PSS)를 네임스페이스에 적용하여 기본적인 보안 정책을 강제하세요
- 서비스 계정에는 필요한 최소한의 RBAC 권한만 부여하세요
- Falco 같은 런타임 보안 도구를 사용하면 실시간으로 이상 행동을 탐지할 수 있습니다
6. CICD 파이프라인 보안
"Git에 push만 했는데 프로덕션에 바로 배포됐어요!" 김개발 씨가 당황한 목소리로 말했습니다. 실수로 민감한 API 키가 포함된 코드를 올렸는데, 자동화된 파이프라인이 그대로 배포해버린 것입니다.
박시니어 씨가 고개를 저었습니다. "CI/CD 파이프라인에 보안 게이트가 없으면 이런 일이 생겨요.
자동화는 좋지만, 보안 검사도 자동화해야 합니다."
CI/CD 파이프라인 보안은 코드가 배포되기까지의 자동화된 과정에서 보안 검사를 통합하는 것입니다. 마치 공장의 품질 검사 라인처럼, 각 단계에서 보안 문제가 있는 코드는 다음 단계로 넘어가지 못하게 차단합니다.
시크릿 스캔, 의존성 검사, 이미지 스캔 등을 파이프라인에 녹여 자동화된 보안을 구현합니다.
다음 코드를 살펴봅시다.
# GitHub Actions 보안 파이프라인 예시 (YAML)
# .github/workflows/security-pipeline.yml
name: Security Pipeline
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
# 시크릿 스캔 - 하드코딩된 비밀 탐지
- name: Secret Scanning
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
# 의존성 취약점 스캔
- name: Dependency Check
run: |
pip install safety
safety check -r requirements.txt --full-report
# Docker 이미지 스캔
- name: Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.IMAGE_NAME }}'
severity: 'CRITICAL,HIGH'
exit-code: '1' # 취약점 발견 시 파이프라인 실패
# SAST - 정적 코드 분석
- name: Static Analysis
run: |
pip install bandit
bandit -r src/ -f json -o bandit-report.json
현대 개발에서 CI/CD는 필수입니다. 코드를 푸시하면 자동으로 테스트하고, 빌드하고, 배포합니다.
하지만 이 자동화가 보안을 건너뛰면 어떻게 될까요? 박시니어 씨가 화이트보드에 그림을 그렸습니다.
"CI/CD 파이프라인을 고속도로라고 생각해보세요. 코드가 차량이고, 목적지는 프로덕션이에요.
고속도로가 빠른 건 좋지만, 중간에 검문소 없이 그냥 달리게 두면 위험한 차량도 목적지에 도착하게 됩니다." 시크릿 스캔은 첫 번째 검문소입니다. 개발자들이 실수로 코드에 하드코딩하는 API 키, 비밀번호, 인증 토큰 등을 탐지합니다.
TruffleHog는 Git 히스토리 전체를 검사하여 한 번이라도 커밋된 적 있는 시크릿을 찾아냅니다. 놀랍게도 많은 보안 사고가 GitHub에 공개된 시크릿에서 시작됩니다.
의존성 검사는 두 번째 검문소입니다. 우리가 사용하는 오픈소스 라이브러리에도 취약점이 있을 수 있습니다.
Safety는 Python 패키지를, npm audit은 Node.js 패키지를 검사합니다. Log4j 사태를 기억하시나요?
의존성 검사가 있었다면 더 빨리 대응할 수 있었을 것입니다. 컨테이너 스캔은 세 번째 검문소입니다.
앞서 배운 Trivy를 파이프라인에 통합합니다. exit-code: '1' 설정이 핵심인데, 이것은 CRITICAL이나 HIGH 취약점이 발견되면 파이프라인을 실패시킵니다.
즉, 취약한 이미지는 배포되지 않습니다. **SAST(Static Application Security Testing)**는 네 번째 검문소입니다.
Bandit은 Python 코드에서 SQL 인젝션, 하드코딩된 비밀번호, 안전하지 않은 함수 사용 등을 탐지합니다. 코드가 실행되기 전에 문제를 발견할 수 있습니다.
실무에서는 이 모든 검사를 PR 단계에서 실행합니다. 즉, 코드가 main 브랜치에 병합되기 전에 검사합니다.
보안 문제가 있으면 PR이 승인되지 않으므로, 문제 있는 코드가 메인 코드베이스에 들어가는 것을 원천 차단합니다. 주의할 점은 **거짓 양성(False Positive)**입니다.
보안 도구들이 실제로는 문제가 아닌 것을 문제라고 보고하는 경우가 있습니다. 모든 경고에 파이프라인을 중단시키면 개발 속도가 크게 저하됩니다.
따라서 팀에서 허용 가능한 위험 수준을 정하고, 심각도에 따라 차등 대응하는 것이 좋습니다. 김개발 씨는 팀의 CI/CD 파이프라인에 보안 게이트를 추가했습니다.
처음에는 경고가 너무 많아서 당황했지만, 하나씩 해결해나가니 점점 줄어들었습니다. 이제는 안심하고 코드를 푸시할 수 있게 되었습니다.
보안 검사가 자동으로 지켜주니까요.
실전 팁
💡 - 시크릿은 절대 코드에 하드코딩하지 말고, GitHub Secrets나 Vault 같은 시크릿 관리 도구를 사용하세요
- 파이프라인 실패 시 Slack 알림을 설정하여 빠르게 대응할 수 있게 하세요
- 정기적으로 보안 도구들을 업데이트하여 최신 취약점 데이터베이스를 유지하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Context Optimization 컨텍스트 최적화 기법
AI 에이전트와 대규모 언어 모델 활용 시 컨텍스트 윈도우를 효율적으로 관리하는 방법을 다룹니다. 토큰 비용 절감부터 캐시 최적화까지, 실무에서 바로 적용할 수 있는 핵심 기법들을 소개합니다.
보안 운영 자동화 완벽 가이드
종합 보안 스캐너부터 SIEM 룰 작성, 위협 헌팅, 인시던트 대응 자동화까지 실무 보안 운영의 모든 것을 다룹니다. 초급 개발자도 이해할 수 있도록 스토리텔링 형식으로 쉽게 설명합니다.
Memory Systems 에이전트 메모리 아키텍처 완벽 가이드
AI 에이전트가 정보를 기억하고 활용하는 메모리 시스템의 핵심 아키텍처를 다룹니다. 벡터 스토어의 한계부터 Knowledge Graph, Temporal Knowledge Graph까지 단계별로 이해할 수 있습니다.
Phase 5 취약점 발굴과 분석 완벽 가이드
보안 전문가가 되기 위한 취약점 발굴의 핵심 기법을 다룹니다. 코드 리뷰부터 퍼징, 바이너리 분석까지 실무에서 바로 활용할 수 있는 기술을 초급자 눈높이에 맞춰 설명합니다.
Multi-Agent Patterns 멀티 에이전트 아키텍처 완벽 가이드
여러 AI 에이전트가 협력하여 복잡한 작업을 수행하는 멀티 에이전트 시스템의 핵심 패턴을 다룹니다. 컨텍스트 격리부터 Supervisor, Swarm, Hierarchical 패턴까지 실무에서 바로 적용할 수 있는 아키텍처 설계 원칙을 배웁니다.