11 KiB
11 KiB
network / ingress / TLS 기준
목적
이 문서는 K3s/Kubernetes(1000+ 서비스) 환경에서
- 어떤 Service 타입을 언제 쓸지
- 외부 공개는 Ingress/Gateway 어디로 할지
- TLS를 어디서 종료할지
- 인증서는 누가 발급·회전할지
- NetworkPolicy로 L3/L4 경계를 어떻게 그을지 를 단일 ground truth로 고정한다.
이 문서의 목표는 다음과 같다.
- 외부 attack surface를 최소화한다
ClusterIP/NodePort/LoadBalancer/Ingress의 역할을 섞지 않는다- 모든 Ingress는 cert-manager 발급 TLS + HTTPS redirect + HSTS + TLS 1.2+ 기본
- K3s 기본 Traefik을 유지하되 packaged manifest는 수정하지 않는다
- Keycloak/Vault/DB 같은 민감 컴포넌트의 노출 범위를 manifest로 증명한다
공식 의미 (근거)
- Service 기본 타입은
ClusterIP. 외부 L4 노출은NodePort또는LoadBalancer, 외부 L7은 Ingress 또는 Gateway API. - Ingress v1 API는 GA이지만 spec은 frozen 상태이고, 신규 기능(L4, traffic split, header match)은 Gateway API로 이동 중이다.
- Ingress v1은
spec.ingressClassName필드로 컨트롤러를 선택한다. 이전의kubernetes.io/ingress.classannotation은 deprecated이며 1.22에서 공식 deprecation 고지. - Ingress TLS Secret은 타입이
kubernetes.io/tls이고 data key는tls.crt,tls.key여야 한다.spec.tls[].hosts와rules[].host는 일치해야 한다. - cert-manager는
Issuer/ClusterIssuer,Certificate,CertificateRequest,Order,ChallengeCRD로 구성된다.Certificate가 참조하는secretName에 자동으로kubernetes.io/tlsSecret이 생성·갱신된다. - ACME HTTP-01은 public DNS + 80 reachable 필요. DNS-01은 wildcard(
*.example.com) 발급에 필수이며 DNS provider API credential이 요구된다. - Traefik v2/v3는
IngressRoute(CRD) +Middleware(CRD)로 L7 정책(redirect, HSTS, rate-limit, auth)을 체계적으로 구성한다. 기본 Ingress API도 annotation으로 일부 기능을 쓸 수 있다. - K3s는 Traefik을 packaged component로 설치한다(
/var/lib/rancher/k3s/server/manifests/traefik.yaml). packaged manifest 직접 수정은 재설치 시 덮어쓰인다.HelmChartConfig로 override한다. - NetworkPolicy는 CNI가 지원해야 enforce된다. K3s 기본 flannel + kube-router policy controller는 v1 NetworkPolicy를 지원한다.
기본 규칙
1. 기본 Service 타입은 ClusterIP
- 내부 통신:
ClusterIP - 외부 HTTP/HTTPS: Ingress
- 외부 TCP/UDP L4:
LoadBalancer(ServiceLB/MetalLB/클라우드 LB 전제) NodePort는 개발/bootstrap 용도 외 운영 금지. namespaceResourceQuota.services.nodeports: 0으로 선제 차단.
2. 모든 Service는 named port + appProtocol
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
appProtocol: http
name: http|https|grpc|metrics로 명명.appProtocol명시는 Ingress controller/서비스 메시가 L7 처리를 최적화할 수 있게 한다.- container
ports[].name과 ServicetargetPort를 이름으로 연결해 포트 번호 drift를 방지.
3. Ingress는 spec.ingressClassName: traefik 필수
kubernetes.io/ingress.classannotation은 deprecated. 어떤 Ingress에도 남기지 않는다.- 컨트롤러가 여러 개인 클러스터(예: Traefik + internal-only NGINX)는
IngressClass리소스를 만들어 class를 명시한다. - 기본 클래스는
ingressclass.kubernetes.io/is-default-class: "true"annotation으로 한 개만 지정.
4. 외부 HTTPS는 cert-manager ClusterIssuer로 발급
- 운영 공인 도메인:
letsencrypt-prodClusterIssuer(ACME HTTP-01) 기본. - Wildcard/internal CA: DNS-01(
letsencrypt-prod-dns) 또는 Vault PKI issuer. - Staging 검증:
letsencrypt-stagingClusterIssuer로 선행 테스트 후 prod 전환. - Ingress에는 annotation으로 issuer 지정:
cert-manager.io/cluster-issuer: letsencrypt-prod. cert-manager가 Certificate + Secret을 자동 생성·갱신한다. - Certificate CRD를 명시적으로 선언하는 방식도 허용(공유 Secret 재사용, 세밀한
duration/renewBefore제어 필요 시).
5. HTTPS redirect + HSTS + TLS 1.2+ 기본
- 모든 외부 Ingress는 HTTP → HTTPS 영구 리다이렉트.
- HSTS:
max-age=31536000; includeSubDomains; preload기본. - TLS minVersion:
VersionTLS12(가능하면 1.3). 취약 cipher(RC4, 3DES) disable. - Traefik에서는
Middleware(redirectScheme, headers) +TLSOptionCRD로 정책을 선언. Ingress annotation 방식 예:traefik.ingress.kubernetes.io/router.entrypoints: websecuretraefik.ingress.kubernetes.io/router.middlewares: default-hsts@kubernetescrd,default-https-redirect@kubernetescrdtraefik.ingress.kubernetes.io/router.tls: "true"
6. Ingress host는 환경별로 분리, wildcard 남용 금지
- dev/staging/prod별 호스트 분리(
auth.dev.example.com,auth.staging.example.com,auth.example.com). - Wildcard 인증서는 플랫폼 수준 Certificate로 관리하고 서비스 Ingress가
secretName재사용. defaultBackend(host 없음) 금지. host가 명시된 rule만 허용.
7. 외부 공개 범위 = "반드시 공개해야 하는 path"만
- Keycloak:
/realms/,/resources/,/.well-known/만 노출./admin/,/metrics,/health는 공개 금지. - 관리 포트(Keycloak 9000, Vault 8201, Postgres 5432, Redis 6379)는 Ingress 경유 금지.
- 내부 도구(Argo CD, Grafana, Kibana)는 VPN/zero-trust proxy(예: Pomerium, cloudflared tunnel)로만 노출.
8. TLS 종료 위치와 내부 재암호화 정책
- 기본: Ingress(Traefik)에서 TLS 종료, 내부 Pod까지는 ClusterIP 경유 평문.
- 민감 backend(Vault, Keycloak token endpoint)는 Ingress→Pod 재암호화 검토. Traefik
serversTransport+insecureSkipVerify: false로 backend TLS 사용. - E2E mTLS가 필요하면 서비스 메시(Linkerd/Istio) 도입을 별도 ADR로 결정.
9. K3s 기본 Traefik은 유지·격리
- packaged manifest(
traefik.yaml) 직접 수정 금지. - 커스터마이징은
HelmChartConfig(kind: HelmChartConfiginhelm.cattle.io/v1)로 override. - Traefik은
ingress-traefiknamespace에 격리, PSAbaseline, NetworkPolicy는 80/443/8443 inbound + 모든 app namespace outbound 허용.
10. ServiceLB(klipper-lb) / MetalLB 결정
- 단일 노드 또는 on-prem 초기: K3s ServiceLB.
- 다중 노드 + BGP/ARP 정책이 필요: MetalLB(
kubectl get deploy -n kube-system | grep servicelb가 없어야 함,--disable=servicelb로 off). - 클라우드(EKS/GKE/AKS): cloud-provider LoadBalancer가 우선.
- 이 결정이 ADR로 고정되기 전에는
LoadBalancerService를 새로 만들지 않는다.
11. NetworkPolicy는 namespace default-deny 기본
모든 운영 namespace는 다음 3종 + 서비스별 allow가 기본 세트다.
default-deny-all(ingress+egress)allow-dns-egress(tokube-systemk8s-app=kube-dns, 53/UDP+TCP)allow-from-ingress-traefik(특정 app Pod만 허용)
12. NetworkPolicy from/to 엔트리 AND/OR 규칙
- 동일 엔트리 내
namespaceSelector+podSelector→ AND(교집합). 권장 패턴. - 별도 엔트리로 분리 → OR(합집합). 거의 항상 버그.
ipBlock은 같은 엔트리 내namespaceSelector/podSelector와 함께 쓸 수 없다. 외부 CIDR allow는 별도 엔트리.
13. egress NetworkPolicy는 DNS 먼저, 서비스별 allow 나중
default-deny-all만 적용하면 DNS 해석 실패로 앱이 기동 불가.- kube-dns 53/UDP+TCP가 첫 번째 allow.
- 외부 API(OIDC issuer, SMTP, S3)는 FQDN이 아니라 IP CIDR로 나와야 v1 NetworkPolicy로 표현 가능. FQDN 기반 egress가 필요하면 Cilium
CiliumNetworkPolicy또는 egress gateway 검토.
14. Prometheus scrape는 ingress rule로 열기
monitoringnamespace의 Prometheus Pod만 허용.namespaceSelector: kubernetes.io/metadata.name=monitoring+podSelector: app.kubernetes.io/name=prometheusAND.- 포트는
metrics(9090/9100 등) 전용, 앱http포트 재사용 금지.
15. Gateway API는 단계적 도입
- 신규 요구사항(traffic split, header routing, gRPC filter)이 Ingress v1으로 표현 불가하면 Gateway API 검토.
- 전환은 서비스 단위로 Ingress →
HTTPRoute로 마이그레이션.GatewayClass/Gateway는 platform-team 소유.
16. health/metrics/admin endpoint 외부 공개 금지
/actuator/*,/debug/pprof/*,/admin/*,/metrics는 Ingress path에 포함하지 않는다.- 별도 Service 포트(
name: metrics)를 만들고 NetworkPolicy로 Prometheus만 허용.
17. Ingress 경로 설계는 prefix + 명시 + 최소
pathType: Prefix명시(ImplementationSpecific금지)./를 바로 노출하기 전 사용자 경로만 선언 가능한지 검토(Keycloak 패턴 참조).- Path rewrite가 필요하면 Traefik
Middleware.stripPrefix를 사용하고 annotation으로 명시.
18. ExternalName/headless Service는 용도에 맞춰
ExternalName은 클러스터 외부 CNAME alias 용도. 인증/TLS 경계와 별개 고려.- Headless(
clusterIP: None)는 StatefulSet DNS, client-side LB 용도. Ingress 대상 아님.
19. 현재 스택 기본 권장안
auth-server / test-server
- Service:
ClusterIPwith namedhttp,metrics - Ingress:
ingressClassName: traefik, cert-managerletsencrypt-prod, HSTS + HTTPS redirect - NetworkPolicy: default-deny + dns + ingress-traefik + db + vault + prometheus
keycloak
- Service:
ClusterIP, namedhttp(8080),management(9000) - Ingress:
/realms/,/resources/,/.well-known/만 노출. 9000 포트는 Service로도 cluster 외부 비공개. - Certificate: 전용(
sso.example.com), 전용 TLS Secret
vault / db / migration-flyway
- Ingress 없음. ClusterIP only. 접근은 bastion +
kubectl port-forward또는 zero-trust proxy.
minio
- API/Console Ingress 분리. Console은 내부 전용, API는 필요 시 signed URL 중심.
ingress-traefik
ingress-traefiknamespace 격리, PSAbaselineService type=LoadBalancer(ServiceLB/MetalLB) 또는hostPort80/443만
프로젝트 기준 요약
- 기본 Service 타입은
ClusterIP, named port 필수 - 모든 Ingress는
spec.ingressClassName: traefik, annotationkubernetes.io/ingress.class금지 - 모든 외부 HTTPS는 cert-manager ClusterIssuer 발급 + HSTS + HTTP→HTTPS redirect + TLS 1.2+
- Keycloak/Vault/DB 노출 범위는 path/host로 증명, 관리 포트 비공개
- K3s Traefik은 packaged manifest 직접 수정 금지,
HelmChartConfigoverride - NetworkPolicy default-deny + DNS allow + ingress-traefik allow 기본 세트
namespaceSelector+podSelectorAND/OR 차이를 정확히 사용- Gateway API는 단계적 도입, 기존 Ingress 유지