init: 폴더구조 설계 및 인프라 설계

This commit is contained in:
DongHyeonka
2026-07-24 14:31:53 +09:00
parent 34ad612281
commit f9c463f87a
1839 changed files with 323096 additions and 1 deletions
+578
View File
@@ -0,0 +1,578 @@
# network / ingress / TLS 예시
모든 YAML은 `kubectl apply --dry-run=server -f -` 기준 clean. Traefik은 `ingress-traefik` namespace에 IngressClass `traefik`으로 설치되어 있다고 가정한다. cert-manager는 `cert-manager` namespace에 설치되어 있다.
---
## 좋은 예시 1: cert-manager ClusterIssuer (staging + prod) + DNS-01 wildcard
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: platform@example.com
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
ingressClassName: traefik
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: platform@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: traefik
selector:
dnsZones:
- example.com
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: platform@example.com
privateKeySecretRef:
name: letsencrypt-prod-dns-account-key
solvers:
- dns01:
route53:
region: us-east-1
hostedZoneID: Z2FDTNDATAQYW2
selector:
dnsZones:
- example.com
```
**왜 좋은가:**
- Staging issuer로 먼저 발급 테스트(LE rate limit 절약). 검증 후 prod로 교체.
- HTTP-01 solver는 `ingressClassName: traefik`으로 challenge Ingress가 정확히 Traefik만 수락.
- DNS-01 solver는 wildcard(`*.example.com`) 발급에 필수. Route53 hosted zone ID 고정.
---
## 좋은 예시 2: Certificate CRD로 TLS Secret 자동 생성 + Ingress 재사용
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: auth-example-com
namespace: auth-prod
spec:
secretName: auth-example-com-tls
secretTemplate:
annotations:
reflector.v1.k8s.emberstack.com/reflection-allowed: "false"
labels:
app.kubernetes.io/part-of: identity-platform
duration: 2160h # 90d
renewBefore: 360h # 15d
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Always
usages:
- server auth
- digital signature
- key encipherment
dnsNames:
- auth.example.com
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
```
**왜 좋은가:**
- cert-manager가 `auth-example-com-tls`라는 `kubernetes.io/tls` Secret을 자동 생성·회전(15일 전).
- ECDSA P-256 + 회전 정책으로 key lifecycle 관리.
- `usages` 명시로 SAN certificate의 Extended Key Usage 제어.
---
## 좋은 예시 3: Traefik Middleware(HSTS + HTTPS redirect) + TLSOption
```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: https-redirect
namespace: ingress-traefik
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: security-headers
namespace: ingress-traefik
spec:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: strict-origin-when-cross-origin
frameDeny: true
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: modern-tls
namespace: ingress-traefik
spec:
minVersion: VersionTLS12
sniStrict: true
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
curvePreferences:
- CurveP521
- CurveP384
```
**왜 좋은가:**
- HSTS preload 조건(`max-age>=31536000` + `includeSubDomains` + `preload`)을 모두 만족.
- TLS 1.2+ 강제, 취약 cipher 제거. `sniStrict: true`로 SNI 없는 클라이언트 차단.
- 재사용 가능한 platform middleware — 각 namespace Ingress가 참조.
---
## 좋은 예시 4: auth-server Service + Ingress (TLS, HSTS, HTTPS redirect)
```yaml
apiVersion: v1
kind: Service
metadata:
name: auth-server
namespace: auth-prod
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/component: api
app.kubernetes.io/part-of: identity-platform
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
appProtocol: http
- name: metrics
port: 9090
targetPort: metrics
protocol: TCP
appProtocol: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auth-server
namespace: auth-prod
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/part-of: identity-platform
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.options: ingress-traefik-modern-tls@kubernetescrd
traefik.ingress.kubernetes.io/router.middlewares: ingress-traefik-https-redirect@kubernetescrd,ingress-traefik-security-headers@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- auth.example.com
secretName: auth-example-com-tls
rules:
- host: auth.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: auth-server
port:
name: http
```
**왜 좋은가:**
- `ingressClassName: traefik` 필드 사용, deprecated annotation 미사용.
- `cert-manager.io/cluster-issuer` annotation으로 TLS Secret(`auth-example-com-tls`)이 자동 발급.
- Traefik middleware 체인으로 HSTS + HTTPS redirect + TLSOption 적용.
- Service는 named port `http`, `metrics` 분리. Ingress는 `http`만 라우팅, metrics는 NetworkPolicy로 Prometheus만 허용.
---
## 나쁜 예시 1: deprecated ingress.class annotation + TLS 누락
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auth-server
namespace: auth-prod
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: auth.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: auth-server
port:
number: 80
```
**문제:**
- `kubernetes.io/ingress.class` annotation은 1.22부터 deprecated. 일부 컨트롤러는 무시한다.
- `spec.tls` 없음 → 평문 HTTP로 노출. 인증 시스템에는 특히 부적절.
- HSTS/HTTPS redirect 미적용.
- Service 포트를 `number: 80`으로 hard-code. named port drift에 취약.
---
## 좋은 예시 5: Keycloak은 `/realms/`, `/resources/`, `/.well-known/`만 공개
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: sso-example-com
namespace: keycloak
spec:
secretName: sso-example-com-tls
duration: 2160h
renewBefore: 360h
privateKey:
algorithm: ECDSA
size: 256
dnsNames:
- sso.example.com
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
---
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
labels:
app.kubernetes.io/name: keycloak
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: keycloak
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
appProtocol: http
- name: management
port: 9000
targetPort: management
protocol: TCP
appProtocol: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak
namespace: keycloak
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.options: ingress-traefik-modern-tls@kubernetescrd
traefik.ingress.kubernetes.io/router.middlewares: ingress-traefik-https-redirect@kubernetescrd,ingress-traefik-security-headers@kubernetescrd
spec:
ingressClassName: traefik
tls:
- hosts:
- sso.example.com
secretName: sso-example-com-tls
rules:
- host: sso.example.com
http:
paths:
- path: /realms/
pathType: Prefix
backend:
service:
name: keycloak
port:
name: http
- path: /resources/
pathType: Prefix
backend:
service:
name: keycloak
port:
name: http
- path: /.well-known/
pathType: Prefix
backend:
service:
name: keycloak
port:
name: http
```
**왜 좋은가:**
- Keycloak 공식 권장 공개 경로만 노출.
- `/admin/`, `/metrics`, `/health`는 Ingress에 없음 → 외부에서 접근 불가.
- `management`(9000) 포트는 Service에만 존재하고 Ingress에는 없음.
---
## 나쁜 예시 2: host 없는 defaultBackend + admin 노출
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: catch-all
namespace: keycloak
spec:
ingressClassName: traefik
defaultBackend:
service:
name: keycloak
port:
number: 8080
rules:
- http:
paths:
- path: /admin
pathType: Prefix
backend:
service:
name: keycloak
port:
number: 9000
```
**문제:**
- `defaultBackend`가 모든 host의 unmatched 요청을 Keycloak으로 포워딩 → 다른 앱 공격면 확대.
- `/admin`을 관리 포트 9000으로 프록시 → Keycloak 공식 권고 위반, 관리 콘솔 외부 노출.
- TLS/HSTS/redirect 미적용.
---
## 좋은 예시 6: Traefik IngressRoute + Middleware(TLS 1.2, rate-limit, BasicAuth)
```yaml
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: auth-prod
spec:
rateLimit:
average: 100
burst: 200
period: 1s
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: auth-server
namespace: auth-prod
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`auth.example.com`) && PathPrefix(`/api/v1`)
services:
- kind: Service
name: auth-server
port: http
scheme: http
passHostHeader: true
middlewares:
- name: https-redirect
namespace: ingress-traefik
- name: security-headers
namespace: ingress-traefik
- name: rate-limit
namespace: auth-prod
tls:
secretName: auth-example-com-tls
options:
name: modern-tls
namespace: ingress-traefik
```
**왜 좋은가:**
- Traefik CRD 네이티브. match 표현이 강력(Host + PathPrefix 조합, Header match 가능).
- Middleware 체인(HSTS + redirect + rate-limit)을 순서대로 지정.
- `TLSOption`을 Route마다 override 가능(특정 host만 mTLS 요구 등).
---
## 좋은 예시 7: Vault/DB는 Ingress 없이 ClusterIP
```yaml
apiVersion: v1
kind: Service
metadata:
name: vault
namespace: vault
labels:
app.kubernetes.io/name: vault
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: vault
ports:
- name: https
port: 8200
targetPort: https
protocol: TCP
appProtocol: https
- name: cluster
port: 8201
targetPort: cluster
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: identity-postgres
namespace: data-prod
labels:
app.kubernetes.io/name: postgres
app.kubernetes.io/instance: identity-postgres
spec:
type: ClusterIP
clusterIP: None
selector:
app.kubernetes.io/name: postgres
app.kubernetes.io/instance: identity-postgres
ports:
- name: postgres
port: 5432
targetPort: postgres
protocol: TCP
appProtocol: postgresql
```
**왜 좋은가:**
- Vault/Postgres 둘 다 Ingress 없음 → 외부 L7 공격면 0.
- Postgres는 headless(`clusterIP: None`) → StatefulSet Pod에 직접 DNS.
- named port `https`/`postgres` 사용.
---
## 나쁜 예시 3: 운영 DB를 NodePort로 공개
```yaml
apiVersion: v1
kind: Service
metadata:
name: identity-postgres
namespace: data-prod
spec:
type: NodePort
ports:
- port: 5432
targetPort: 5432
nodePort: 30032
```
**문제:**
- 모든 노드의 30032 포트가 외부에서 접근 가능 → DB가 인터넷에 노출될 수 있음.
- TLS/mTLS/NetworkPolicy 어디서도 통제 불가.
- `services.nodeports: 0` quota를 걸어 namespace 단에서 차단해야 한다.
---
## 좋은 예시 8: K3s Traefik HelmChartConfig override
```yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
deployment:
replicas: 3
service:
spec:
externalTrafficPolicy: Local
ports:
web:
redirectTo:
port: websecure
priority: 10
websecure:
tls:
enabled: true
ingressClass:
enabled: true
isDefaultClass: true
additionalArguments:
- "--providers.kubernetesingress.ingressclass=traefik"
- "--metrics.prometheus=true"
- "--entrypoints.websecure.http.tls.options=modern-tls@kubernetescrd"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
```
**왜 좋은가:**
- K3s packaged Traefik manifest는 건드리지 않고, override만 선언적으로 관리.
- replica 3, `externalTrafficPolicy: Local`로 source IP 보존.
- 기본 websecure에 `modern-tls` TLSOption을 묶어 platform 전역 TLS 정책 통일.