CoCo(Confidential Containers)는 신뢰할 수 없는 인프라에서도 워크로드 기밀성을 올릴 수 있지만, 막상 운영에 들어가면 설정 누락/오배포가 잦아 보안이 “구멍 난 채로 배포”되기 쉽습니다. 이 글에서는 Kyverno로 CoCo 배포에 필요한 인프라 의존 설정(wiring)을 정책으로 자동화해 보안을 강제하면서도 개발자 경험을 해치지 않는 균형점을 어떻게 잡을지 정리합니다.
CoCo와 Kyverno: 서로 대체재가 아니라 “레이어가 다른” 조합입니다
먼저 자주 섞이는 개념을 분리해보면 이해가 쉬워집니다.
- CoCo(Confidential Containers): TEE 기반 격리와 attestation을 활용해, 호스트/노드가 신뢰되지 않는 환경에서도 컨테이너 워크로드의 기밀성/무결성 보장 범위를 넓히는 런타임 보안 모델입니다.
- Confidential Containers 오픈소스(GitHub 조직/프로젝트): 위 보안 모델을 Kubernetes에서 쓰기 위해 필요한 **구현체(런타임/구성요소/연동)**를 제공하는 프로젝트입니다.
- Kyverno: CoCo 자체를 “제공”하는 도구가 아니라, Kubernetes에서 정책(Policy as Code)으로 검증/변경/생성을 자동화해 “CoCo를 제대로 쓰도록” 만드는 운영 레이어입니다.
제가 이 조합이 중요하다고 보는 이유는 간단합니다. CoCo는 강력하지만, 현실의 실패 모드는 종종 “TEE가 깨짐”이 아니라 배포 YAML 한 줄 누락, 런타임 클래스 잘못 지정, 필요한 어노테이션/라벨 빠짐 같은 운영 실수에서 시작하기 때문입니다.
왜 CoCo는 ‘자동화’가 필요할까요?
CoCo를 제대로 적용하려면 워크로드가 특정 런타임으로 뜨고, 필요한 메타데이터가 붙고, 특정 네임스페이스/서비스어카운트/이미지 정책과 맞물리는 등 “인프라 의존 wiring”이 생깁니다. 문제는 이 wiring이:
- 애플리케이션 팀 입장에서는 알기 어렵고
- 알아도 배포 시점에 매번 일관되게 적용하기 어렵고
- 한 번 누락되면 “CoCo를 쓴다고 믿었는데 실제로는 평범한 컨테이너로 배포” 같은 치명적인 착시가 생깁니다.
그래서 플랫폼 팀 관점에서는 “CoCo 도입”보다 “CoCo가 항상 켜져 있게 만드는 운영 자동화/강제”가 더 어렵고 더 중요해집니다.
Kyverno로 할 수 있는 3가지: Validate / Mutate / Generate
Kyverno를 CoCo 자동화에 쓰면, 크게 아래 3가지를 조합하게 됩니다.
- Validate(검증): CoCo가 필요한 워크로드가 규칙을 어기면 배포를 거절합니다.
- Mutate(변경/주입): 개발자가 몰라도 되도록 런타임 설정 등을 자동 주입합니다.
- Generate(생성): 네임스페이스별로 필요한 보조 리소스(ConfigMap/Policy/Secret 템플릿 등)를 자동 생성합니다.
여기서 핵심 설계 포인트는 “무조건 막기”가 아니라, **막을 것(하드 가드레일)**과 **자동으로 고쳐줄 것(소프트 가드레일)**을 나누는 것입니다.
예시 1) Mutate로 RuntimeClass 자동 주입(개발자 경험 개선)
예를 들어 “특정 라벨이 있으면 CoCo 런타임을 사용한다”는 팀 내 계약을 만들고, 그에 맞춰 runtimeClassName을 주입할 수 있습니다.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: coco-default-runtimeclass
spec:
rules:
- name: set-runtimeclass-when-coco-enabled
match:
resources:
kinds:
- Pod
preconditions:
all:
- key: "{{ request.object.metadata.labels.\"security.coco/enabled\" || '' }}"
operator: Equals
value: "true"
mutate:
patchStrategicMerge:
spec:
runtimeClassName: kata-qemu-coco
- 개발자는
security.coco/enabled=true만 선언하면 되고, - 플랫폼 팀은 어떤 런타임 클래스가 CoCo용인지 정책에서 일관되게 관리할 수 있습니다.
이 패턴이 좋은 이유는 “보안팀이 원하는 강제”를 하면서도, 개발자가 인프라 디테일을 매번 외우게 만들지 않기 때문입니다.
예시 2) Validate로 “CoCo 필수 워크로드는 반드시 CoCo로만” 강제(보안 보장)
반대로 “이 워크로드는 무조건 CoCo에서 돌아야 한다” 같은 강한 요구가 있으면, 누락/변조를 차단해야 합니다.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-coco-runtime-for-sensitive
spec:
validationFailureAction: Enforce
rules:
- name: sensitive-must-use-coco-runtimeclass
match:
resources:
kinds:
- Pod
preconditions:
all:
- key: "{{ request.object.metadata.labels.\"data.class\" || '' }}"
operator: Equals
value: "sensitive"
validate:
message: "data.class=sensitive 워크로드는 CoCo runtimeClassName을 사용해야 합니다."
pattern:
spec:
runtimeClassName: "kata-qemu-coco"
이렇게 하면 “민감 데이터 처리” 같이 명확한 경계가 있는 워크로드는, 실수로 일반 런타임에 배포되는 순간을 원천 차단할 수 있습니다.
보안 강제 vs 팀 자율성: 제가 권하는 균형점(실전 운영 관점)
처음 질문으로 돌아가서, 보안 강제와 팀 자율성의 균형은 어디가 적절할까요? 저는 아래처럼 “계층화”하는 접근이 가장 현실적이라고 봅니다.
1) 강제(Enforce)는 ‘위협모델이 명확한 경계’에만 씁니다
예: data.class=sensitive, 결제/PII, 키 처리, 모델 가중치 등
이 영역은 “유연함”보다 “사고 방지”가 우선이라 Validate-Enforce가 맞습니다.
2) 나머지는 Mutate로 ‘실수를 줄이는 기본값’을 제공합니다
대부분의 팀/서비스에 “기본은 자동 주입”으로 두면 반발이 적습니다. 개발자는 여전히 YAML을 쓰지만, 인프라 wiring을 외우지 않아도 되기 때문입니다.
3) 예외는 ‘정책으로 허용하되, 절차를 남깁니다’
팀 자율성이 필요한 지점은 분명 있습니다(특정 워크로드는 성능/호환성 때문에 CoCo가 당장 어렵다 등). 다만 예외를 “사람 구두 승인”으로만 두면 결국 우회가 생깁니다.
- 네임스페이스 단위의 예외
- 만료일이 있는 예외 라벨
- 특정 그룹만 정책 변경 가능
처럼 예외도 코드로 관리하는 게 장기적으로 운영 비용을 줄입니다.
4) “Kyverno가 검증됐다”는 말은 ‘보안의 전부’가 아니라 ‘운영 레이어의 신뢰’입니다
대화에서 나온 것처럼 Kyverno는 CNCF 생태계/사례로 성숙도가 높고, “정책을 통해 배포를 통제하는 운영 방식”은 충분히 검증된 편입니다.
다만 Kyverno가 커버하는 것은 주로:
- 설정 누락 방지
- 준수/컴플라이언스 강제
- 배포 일관성 확보
이고, CoCo가 노리는: - 호스트/노드까지 포함한 위협모델에서의 기밀성(TEE)
- attestation 기반 신뢰 수립
과는 레이어가 다릅니다. 결론적으로 **Kyverno는 CoCo의 대체재가 아니라, CoCo가 “항상 켜져 있게 만드는 장치”**에 가깝습니다.
마무리
CoCo 도입의 성패는 “좋은 기술을 선택했는가”보다, 실제 배포에서 CoCo가 빠지지 않도록 운영 실수를 구조적으로 제거했는가에 달려 있습니다. Kyverno로 Validate/Mutate/Generate를 적절히 조합하면, 플랫폼 팀은 보안을 강제하고 애플리케이션 팀은 유연하게 배포하는 균형점을 훨씬 쉽게 만들 수 있습니다.
