refactor: 폴더 구조 변경

This commit is contained in:
donghyeon-ka
2026-08-02 00:22:19 +09:00
parent f9c463f87a
commit be2f8e4863
1869 changed files with 4565 additions and 295591 deletions
+36
View File
@@ -0,0 +1,36 @@
# gitops AGENTS
Role:
- own Kubernetes/K3s source-of-truth manifests and Kustomize composition
- keep catalog bases, environment overlays, cluster entrypoints, and Vault Secrets Operator assets separate
- make render / validate / diff / apply possible from Git without relying on live cluster state
Scope:
- `apps/`: app-owned workload/data/operation catalogs
- `platform/`: shared services and operator catalogs
- `policies/`: security and governance catalogs
- `tenants/`: namespace/RBAC/quota catalogs
- `clusters/<env>/<cluster>/namespaces` and `stages/`: deployable environment composition
- `clusters/<env>/<cluster>/all/`: render/audit aggregate only
Read first:
- `/docs/standards/infra/architecture-environments.md`
- `/docs/standards/infra/kustomize.md`
- `/docs/standards/infra/k3s-specific.md`
- `/docs/standards/infra/operations-runbook-upgrade-rollback.md`
Rules:
- prefer `kubectl kustomize`, `kubectl diff -k`, and `kubectl apply -k`
- apply stage entrypoints only; never apply an environment `all/` aggregate
- do not use server-local manifests as source of truth
- do not place environment differences in a catalog `base/`
- do not place base resource definitions directly in overlays unless the resource is environment-only by design
- do not commit production secret values
- keep scripts as helpers; manifests remain declarative source
Catalog base contract:
- shared labels, selectors, probes, resources, services, storage and policy shape are allowed
- environment hostnames, cluster contexts, production-only sizing, rollout image
references, deletion overrides and secret values are forbidden
- every base must be reusable by lab, staging and prod overlays
- ownership must remain visible in the catalog unit path
+20
View File
@@ -0,0 +1,20 @@
# Project catalog ownership
| Catalog | Ownership |
| --- | --- |
| `apps/auth-server` | API workload와 lab ingress |
| `apps/identity-postgres` | auth platform 전용 PostgreSQL |
| `apps/auth-migration` | versioned Flyway migration Job |
| `apps/keycloak-realm-import` | versioned realm/client import |
| `platform/forward-auth` | oauth2-proxy와 Traefik ForwardAuth component |
| `platform/keycloak` | Keycloak instance와 ingress |
| `platform/minio` | MinIO Tenant |
| `platform/registry` | Docker registry |
| `platform/vault` | Vault workload |
| `platform/secret-delivery` | VSO auth/connection, Helm values와 VaultStaticSecret |
| `platform/*-operator` | cluster-scoped controller source와 environment patch |
| `policies/baseline` | namespace 보안 baseline |
| `tenants/mnt` | `mnt` namespace 경계 |
Catalog는 직접 controller root로 지정하지 않습니다. 실제 환경은
`gitops/clusters/<...>` 아래 entrypoint에서 필요한 catalog만 선택합니다.
+44
View File
@@ -0,0 +1,44 @@
# Project GitOps layout
Project-specific Kubernetes resources are split into reusable catalogs and
cluster entrypoints.
```text
gitops/
├── apps/
│ ├── auth-server/
│ ├── identity-postgres/
│ ├── auth-migration/
│ └── keycloak-realm-import/
├── platform/
│ ├── cert-manager/
│ ├── forward-auth/
│ ├── keycloak/
│ ├── keycloak-operator/
│ ├── minio/
│ ├── registry/
│ ├── secret-delivery/
│ ├── traefik/
│ └── vault/
├── policies/baseline/
├── tenants/mnt/
└── clusters/lab/main/
├── stages/
└── all/
```
Rules:
- Catalog `base`는 환경 중립 shape를 소유하고 `overlays/<env>`는 차이만
표현합니다.
- `clusters/lab/main/stages/*`만 현재 deployable entrypoint입니다.
- `clusters/lab/main/all`은 전체 렌더와 정적 검사 전용이며 apply하지 않습니다.
- operator readiness, Vault init, Secret 생성과 Job 완료는 stage 사이에서
`scripts/bin/bootstrap.sh`가 확인합니다.
- 새 deployable entrypoint는 `tests/kustomize-entrypoints.txt`에 추가합니다.
```bash
kubectl kustomize gitops/clusters/lab/main/all >/tmp/project-infra-lab.yaml
export KUBE_CONTEXT_LAB='<expected-context>'
bash scripts/bin/bootstrap.sh lab
```
+33
View File
@@ -0,0 +1,33 @@
# GitOps Desired State
Kubernetes API 안에서 지속적으로 reconcile할 desired state를 관리합니다.
GitOps controller를 사용하지 않는 초기 단계에도 `kubectl kustomize`로 같은
entrypoint를 렌더할 수 있습니다.
```text
platform ─┐
policies ─┼──▶ clusters/<...> ◀── GitOps controller root
tenants ─┤
apps ─┘
```
- `clusters`: 클러스터별 최종 조립점
- `platform`: cluster-wide addon과 controller
- `policies`: cluster-wide admission과 거버넌스 규칙
- `tenants`: 구체적인 namespace/RBAC/quota/NetworkPolicy
- `apps`: application 배포 정의
Catalog 디렉터리를 controller root로 직접 지정하지 않습니다. cluster entrypoint가
필요한 base/overlay를 선택하고 의존 순서를 명시합니다.
## 기본 규칙
- `base`는 환경을 모르며 재사용 가능한 기본값만 가집니다.
- `overlays`는 차이만 patch하고 전체 manifest를 복사하지 않습니다.
- CRD/controller가 필요한 리소스는 controller 이후에 reconcile합니다.
- resource namespace, ownership label과 버전을 명시합니다.
- raw `Secret` 또는 실제 비밀값을 커밋하지 않습니다.
- 원격 base/chart를 참조할 때 immutable version 또는 digest를 사용합니다.
Flux/Argo CD 고유 리소스와 sync ordering은 선택한 controller를 기록한 ADR에
문서화합니다.
+6
View File
@@ -0,0 +1,6 @@
# gitops/apps AGENTS
Application-owned workloads, stateful dependencies, and independently runnable
operations live here. Keep environment-neutral manifests in each unit's `base`.
Ingress hosts, image rollout references, environment labels, storage classes,
retention, and Secret delivery differences belong in `overlays/<env>`.
+18
View File
@@ -0,0 +1,18 @@
# Application Deployment Catalog
애플리케이션의 source code가 아니라 Kubernetes 배포 정의를 둡니다. app 팀이
별도 source/deploy 저장소를 소유하면 이곳에는 immutable artifact를 참조하는
GitOps 리소스만 둘 수 있습니다.
```text
apps/
└── example-api/
├── base/
└── overlays/
├── dev/
└── prod/
```
base는 환경을 모르고, overlay에는 replica/resource/config처럼 필요한 차이만
둡니다. image는 mutable tag 대신 조직 정책에 따른 고정 tag 또는 digest를
사용합니다.
+22
View File
@@ -0,0 +1,22 @@
# __REPLACE_ME_APPLICATION_NAME__
## 소유자
Team: __REPLACE_ME_OWNER__
repository와 on-call 정보를 적습니다.
## 배포 계약
- Namespace:
- Image/artifact source:
- Ports/protocol:
- Dependency:
- SLO/alerts:
## 구성
- `base`: 공통 Kubernetes 배포 정의
- `overlays`: 환경별 replica, resource, config 차이
비밀은 ExternalSecret 같은 참조 또는 승인된 암호화 형식으로만 추가합니다.
+4
View File
@@ -0,0 +1,4 @@
# Base
환경을 모르는 application의 공통 manifest를 둡니다. namespace 자체의 소유권이
tenant catalog에 있다면 이곳에서 중복 생성하지 않습니다.
@@ -0,0 +1,4 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: []
+6
View File
@@ -0,0 +1,6 @@
# Overlays
필요한 환경에만 overlay를 추가합니다. 각 overlay는 `../../base`를 참조하고
환경별 patch만 포함합니다.
비밀값, 임시 debug 설정과 수동 hotfix 결과를 overlay에 커밋하지 않습니다.
+5
View File
@@ -0,0 +1,5 @@
# gitops/apps/auth-migration AGENTS
Jobs and CronJobs live here. Operations are versioned, independently runnable
units and must not be hidden inside an application base. Define completion,
retry, timeout, rollback, and rerun semantics before changing them.
@@ -0,0 +1,80 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-server-migrate-sql
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
data:
V1__create_users_table.sql: |
create schema if not exists auth;
create or replace function auth.set_updated_at()
returns trigger as $$
begin
new.updated_at := current_timestamp;
return new;
end;
$$ language plpgsql;
create table auth.users (
id uuid not null,
email text not null,
encoded_password text not null,
name text not null,
provider text not null,
created_at timestamp with time zone not null default current_timestamp,
updated_at timestamp with time zone not null default current_timestamp,
constraint pk_users primary key (id),
constraint uq_users__email unique (email),
constraint ck_users__provider check (provider in ('LOCAL', 'GOOGLE', 'GITHUB'))
);
create index ix_users__created_at on auth.users (created_at);
create trigger trg_users__set_updated_at
before update on auth.users
for each row
when (old.* is distinct from new.*)
execute function auth.set_updated_at();
V2__add_oauth_login_columns.sql: |
alter table auth.users alter column encoded_password drop not null;
alter table auth.users add column provider_subject text;
alter table auth.users
add constraint uq_users__provider_provider_subject unique (provider, provider_subject);
V3__add_user_provider_field_constraints.sql: |
alter table auth.users
add constraint ck_users__local_password_required
check (
(provider = 'LOCAL' and encoded_password is not null and provider_subject is null)
or (provider <> 'LOCAL')
);
alter table auth.users
add constraint ck_users__social_subject_required
check (
(provider <> 'LOCAL' and provider_subject is not null and encoded_password is null)
or (provider = 'LOCAL')
);
V4__add_keycloak_provider.sql: |
alter table auth.users drop constraint ck_users__provider;
alter table auth.users
add constraint ck_users__provider check (provider in ('LOCAL', 'KEYCLOAK', 'GOOGLE', 'GITHUB'));
V5__keycloak_only_provider.sql: |
alter table auth.users drop constraint if exists ck_users__local_password_required;
alter table auth.users drop constraint if exists ck_users__social_subject_required;
alter table auth.users drop constraint if exists ck_users__provider;
alter table auth.users drop column if exists encoded_password;
alter table auth.users alter column provider_subject set not null;
alter table auth.users
add constraint ck_users__provider check (provider = 'KEYCLOAK');
+119
View File
@@ -0,0 +1,119 @@
apiVersion: batch/v1
kind: Job
metadata:
name: auth-server-migrate-0-1-0
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
parallelism: 1
completions: 1
backoffLimit: 0
activeDeadlineSeconds: 1800
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
serviceAccountName: auth-server-migrate-sa
automountServiceAccountToken: false
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
initContainers:
- name: flyway-info
image: flyway/flyway:10.20.1
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args: ["FLYWAY_USER=\"$(cat /run/secrets/db/SPRING_DATASOURCE_USERNAME)\" FLYWAY_PASSWORD=\"$(cat /run/secrets/db/SPRING_DATASOURCE_PASSWORD)\" exec /flyway/flyway info"]
env: &flywayEnv
- name: FLYWAY_URL
value: jdbc:postgresql://identity-postgres:5432/auth_server
- name: FLYWAY_LOCATIONS
value: filesystem:/flyway/sql
- name: FLYWAY_SCHEMAS
value: auth
- name: FLYWAY_DEFAULT_SCHEMA
value: auth
- name: FLYWAY_TABLE
value: flyway_schema_history
- name: FLYWAY_VALIDATE_ON_MIGRATE
value: "true"
- name: FLYWAY_BASELINE_ON_MIGRATE
value: "false"
- name: FLYWAY_OUT_OF_ORDER
value: "false"
- name: FLYWAY_MIXED
value: "false"
- name: FLYWAY_CLEAN_DISABLED
value: "true"
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
securityContext: &flywaySC
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts: &flywayVM
- name: sql
mountPath: /flyway/sql
readOnly: true
- name: db-secret
mountPath: /run/secrets/db
readOnly: true
- name: tmp
mountPath: /tmp
containers:
- name: flyway-migrate
image: flyway/flyway:10.20.1
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args: ["FLYWAY_USER=\"$(cat /run/secrets/db/SPRING_DATASOURCE_USERNAME)\" FLYWAY_PASSWORD=\"$(cat /run/secrets/db/SPRING_DATASOURCE_PASSWORD)\" exec /flyway/flyway migrate"]
env: *flywayEnv
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
securityContext: *flywaySC
volumeMounts: *flywayVM
volumes:
- name: sql
configMap:
name: auth-server-migrate-sql
- name: db-secret
secret:
secretName: auth-server-db
defaultMode: 0400
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 128Mi
@@ -0,0 +1,16 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- configmap-sql.yaml
- job.yaml
labels:
- pairs:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: auth-server-migrate-sa
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
automountServiceAccountToken: false
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- networkpolicy.yaml
@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: auth-server-migrate-egress-postgres
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
app.kubernetes.io/component: migration
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
ports:
- protocol: TCP
port: 5432
@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-server-config
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
data:
SPRING_PROFILES_ACTIVE: dev
SPRING_DATASOURCE_URL: jdbc:postgresql://identity-postgres:5432/auth_server
APP_DATASOURCE_URL: jdbc:postgresql://identity-postgres:5432/auth_server
MANAGEMENT_SERVER_ADDRESS: 0.0.0.0
MANAGEMENT_SERVER_PORT: "8081"
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,prometheus
SERVER_PORT: "8080"
@@ -0,0 +1,128 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-server
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
replicas: 1
revisionHistoryLimit: 5
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
template:
metadata:
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
serviceAccountName: auth-server-sa
automountServiceAccountToken: false
terminationGracePeriodSeconds: 45
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containers:
- name: auth-server
image: registry.example.com/auth-platform/auth-server:0.1.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: metrics
containerPort: 8081
protocol: TCP
envFrom:
- configMapRef:
name: auth-server-config
- secretRef:
name: auth-server-db
env:
- name: JAVA_TOOL_OPTIONS
value: "-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"
- name: SPRING_CONFIG_IMPORT
value: "optional:configtree:/etc/secrets/"
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
memory: 768Mi
startupProbe:
httpGet:
path: /actuator/health/liveness
port: metrics
periodSeconds: 5
failureThreshold: 24
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: metrics
periodSeconds: 5
failureThreshold: 3
timeoutSeconds: 2
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: metrics
periodSeconds: 15
failureThreshold: 3
timeoutSeconds: 3
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: db-creds
mountPath: /etc/secrets
readOnly: true
- name: tmp
mountPath: /tmp
volumes:
- name: db-creds
secret:
secretName: auth-server-db
defaultMode: 0400
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 128Mi
@@ -0,0 +1,17 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- configmap.yaml
- deployment.yaml
- service.yaml
labels:
- pairs:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
+27
View File
@@ -0,0 +1,27 @@
apiVersion: v1
kind: Service
metadata:
name: auth-server
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
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: 8081
targetPort: metrics
protocol: TCP
appProtocol: http
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: auth-server-sa
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
automountServiceAccountToken: false
@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auth-server
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/version: "0.1.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: kube-system-security-headers@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: auth.local.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: auth-server
port:
name: http
@@ -0,0 +1,40 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- ingress.yaml
- networkpolicy.yaml
images:
- name: registry.example.com/auth-platform/auth-server
newName: registry.project.com/auth-platform/auth-server
newTag: manual-20260512071751
patches:
- target:
kind: ConfigMap
name: auth-server-config
patch: |-
- op: add
path: /data/APP_SECURITY_KEYCLOAK_ISSUER_URI
value: http://keycloak.local.test/realms/platform
- op: add
path: /data/SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI
value: http://keycloak.local.test/realms/platform
- op: add
path: /data/SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI
value: http://keycloak/realms/platform/protocol/openid-connect/certs
- op: add
path: /data/SERVER_MAX_HTTP_REQUEST_HEADER_SIZE
value: 64KB
- target:
kind: ServiceAccount
name: auth-server-sa
patch: |-
- op: add
path: /imagePullSecrets
value:
- name: docker-registry-pull-credentials
@@ -0,0 +1,63 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: auth-server-ingress-traefik
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: auth-server-egress
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
ports:
- protocol: TCP
port: 8080
+5
View File
@@ -0,0 +1,5 @@
# gitops/apps/identity-postgres AGENTS
Stateful data services live here. Read the storage, backup, security, and
component standard before changing a StatefulSet, Tenant, PVC, or retention
policy. Environment storage classes and deletion behavior belong in overlays.
@@ -0,0 +1,30 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: identity-postgres-initdb
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
data:
01-create-databases.sh: |
#!/usr/bin/env bash
set -euo pipefail
KEYCLOAK_DB_PASSWORD="$(cat /run/secrets/keycloak-db/password)"
AUTH_SERVER_DB_PASSWORD="$(cat /run/secrets/auth-server-db/SPRING_DATASOURCE_PASSWORD)"
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-'SQL'
CREATE ROLE keycloak LOGIN;
CREATE DATABASE keycloak OWNER keycloak;
CREATE ROLE auth_server LOGIN;
CREATE DATABASE auth_server OWNER auth_server;
SQL
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" \
-c "ALTER ROLE keycloak PASSWORD '$(printf '%s' "$KEYCLOAK_DB_PASSWORD" | sed "s/'/''/g")';"
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" \
-c "ALTER ROLE auth_server PASSWORD '$(printf '%s' "$AUTH_SERVER_DB_PASSWORD" | sed "s/'/''/g")';"
@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- configmap-initdb.yaml
- service-headless.yaml
- service.yaml
- statefulset.yaml
labels:
- pairs:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Service
metadata:
name: identity-postgres-headless
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
clusterIP: None
selector:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
ports:
- name: postgres
port: 5432
targetPort: postgres
protocol: TCP
appProtocol: postgresql
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Service
metadata:
name: identity-postgres
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
ports:
- name: postgres
port: 5432
targetPort: postgres
protocol: TCP
appProtocol: postgresql
@@ -0,0 +1,12 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: identity-postgres-sa
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
automountServiceAccountToken: false
@@ -0,0 +1,174 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: identity-postgres
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
replicas: 1
serviceName: identity-postgres-headless
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
selector:
matchLabels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
template:
metadata:
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
serviceAccountName: identity-postgres-sa
automountServiceAccountToken: false
terminationGracePeriodSeconds: 60
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
fsGroup: 999
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containers:
- name: postgres
image: postgres:16.4
imagePullPolicy: IfNotPresent
ports:
- name: postgres
containerPort: 5432
protocol: TCP
env:
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_USER_FILE
value: /run/secrets/superuser/username
- name: POSTGRES_PASSWORD_FILE
value: /run/secrets/superuser/password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: KEYCLOAK_DB_PASSWORD_FILE
value: /run/secrets/keycloak-db/password
- name: AUTH_SERVER_DB_PASSWORD_FILE
value: /run/secrets/auth-server-db/SPRING_DATASOURCE_PASSWORD
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "1"
memory: 1Gi
startupProbe:
exec:
command:
- sh
- -c
- pg_isready -U "$(cat "$POSTGRES_USER_FILE")"
periodSeconds: 5
failureThreshold: 60
timeoutSeconds: 3
readinessProbe:
exec:
command:
- sh
- -c
- pg_isready -U "$(cat "$POSTGRES_USER_FILE")"
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
livenessProbe:
exec:
command:
- sh
- -c
- pg_isready -U "$(cat "$POSTGRES_USER_FILE")"
periodSeconds: 30
failureThreshold: 3
timeoutSeconds: 3
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: initdb
mountPath: /docker-entrypoint-initdb.d
readOnly: true
- name: superuser
mountPath: /run/secrets/superuser
readOnly: true
- name: keycloak-db
mountPath: /run/secrets/keycloak-db
readOnly: true
- name: auth-server-db
mountPath: /run/secrets/auth-server-db
readOnly: true
- name: tmp
mountPath: /tmp
- name: run
mountPath: /var/run/postgresql
volumes:
- name: initdb
configMap:
name: identity-postgres-initdb
defaultMode: 0755
- name: superuser
secret:
secretName: identity-postgres-superuser
defaultMode: 0400
- name: keycloak-db
secret:
secretName: keycloak-db
defaultMode: 0400
- name: auth-server-db
secret:
secretName: auth-server-db
defaultMode: 0400
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 128Mi
- name: run
emptyDir:
medium: Memory
sizeLimit: 64Mi
volumeClaimTemplates:
- metadata:
name: data
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/version: "16.4"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
accessModes:
- ReadWriteOnce
storageClassName: replace-in-overlay
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
@@ -0,0 +1,23 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- networkpolicy.yaml
patches:
- target:
kind: StatefulSet
name: identity-postgres
patch: |-
- op: replace
path: /spec/persistentVolumeClaimRetentionPolicy/whenDeleted
value: Delete
- op: replace
path: /spec/persistentVolumeClaimRetentionPolicy/whenScaled
value: Delete
- op: replace
path: /spec/volumeClaimTemplates/0/spec/storageClassName
value: local-path
@@ -0,0 +1,34 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: identity-postgres-ingress-clients
labels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
app.kubernetes.io/component: database
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
- podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
- podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-migrate-0-1-0
ports:
- protocol: TCP
port: 5432
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- platform-realm-import.yaml
@@ -0,0 +1,62 @@
apiVersion: k8s.keycloak.org/v2beta1
kind: KeycloakRealmImport
metadata:
name: platform-realm-v1
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/version: "26.6.1"
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
keycloakCRName: keycloak
placeholders:
AUTH_SERVER_INGRESS_CLIENT_SECRET:
secret:
name: keycloak-client-auth-server-ingress
key: client-secret
realm:
realm: platform
enabled: true
sslRequired: external
registrationAllowed: true
loginWithEmailAllowed: true
accessTokenLifespan: 300
defaultRoles:
- user
roles:
realm:
- name: user
clients:
- clientId: auth-server-ingress
name: auth-server-ingress
protocol: openid-connect
publicClient: false
clientAuthenticatorType: client-secret
secret: ${AUTH_SERVER_INGRESS_CLIENT_SECRET}
standardFlowEnabled: true
directAccessGrantsEnabled: false
serviceAccountsEnabled: false
defaultClientScopes:
- profile
- email
- roles
protocolMappers:
- name: realm roles in id token
protocol: openid-connect
protocolMapper: oidc-usermodel-realm-role-mapper
config:
user.attribute: foo
claim.name: realm_access.roles
jsonType.label: String
multivalued: "true"
access.token.claim: "true"
id.token.claim: "true"
introspection.token.claim: "true"
redirectUris:
- http://auth.local.test/oauth2/callback
webOrigins:
- http://auth.local.test
rootUrl: http://auth.local.test
baseUrl: http://auth.local.test
+45
View File
@@ -0,0 +1,45 @@
# gitops/clusters AGENTS
Role:
- own environment-specific Kustomize composition
- express only the differences between environments and reusable bases
- make environment-level render, diff, apply, audit, and GitOps sync straightforward
Scope:
- `<environment>/<region-or-cluster>/stages/`
- `<environment>/<region-or-cluster>/all/`
Allowed:
- namespace selection
- replicas
- resource requests/limits overrides
- image tags or image references
- ingress host/TLS differences
- environment-specific patches
- environment-specific secret references
- storage class and retention differences
Forbidden:
- copying full base manifests into overlays
- redefining base resources wholesale without a clear environment-only reason
- placing production secret values in Git
- changing service ownership or workload kind without updating the base and standards
- mixing multiple environments in one overlay
Read first:
- `/docs/standards/infra/kustomize.md`
- `/docs/standards/infra/architecture-environments.md`
- `/docs/standards/infra/config-and-secrets.md`
- `/docs/standards/infra/network-ingress-tls.md`
- `/docs/standards/infra/resources-probes-availability.md`
- `/docs/standards/infra/security-hardening.md`
- `/docs/standards/infra/operations-runbook-upgrade-rollback.md`
Rules:
- cluster entrypoints are environment-first by design
- `stages/*` are deployable entrypoints; `all/` is validation-only
- controller readiness and one-shot completion belong between stage applies
- keep patches small and named by target and intent
- render and diff the target environment before apply
- prod overlays must be the most conservative environment
- if an overlay starts re-declaring most of a resource, move common shape back into its catalog `base`
+37
View File
@@ -0,0 +1,37 @@
# Cluster Entrypoints
클러스터별 최종 desired state 진입점입니다. GitOps controller의 root는 이
디렉터리 아래 **하나의 실제 cluster leaf**만 가리킵니다.
소규모:
```text
clusters/
└── dev/
└── main/
└── kustomization.yaml
```
다중 환경·리전:
```text
clusters/
├── dev/ap-northeast-2/dev-a/
├── staging/ap-northeast-2/staging-a/
└── prod/
├── ap-northeast-2/prod-a/
└── ap-southeast-1/prod-b/
```
cluster leaf에는 catalog 구현을 복사하지 않고 선택 목록과 cluster 고유 patch만
둡니다. 계정 또는 조직 경계가 필요하면 경로 segment를 추가할 수 있지만 자동화가
고정 depth에 의존하지 않도록 합니다.
`_template`을 실제 경로로 복사해 시작합니다.
## Current lab exception
`lab/main`은 CRD, Vault, Secret과 one-shot operation의 readiness 경계 때문에
단일 direct leaf 대신 `namespaces`와 ordered `stages/*` leaf를 사용합니다.
현재는 `scripts/bin/bootstrap.sh`가 순서를 보장합니다. `all/`은 감사
aggregate이므로 controller root로 지정하거나 apply하지 않습니다.
+31
View File
@@ -0,0 +1,31 @@
# __REPLACE_ME_CLUSTER_NAME__
## 대상
- Environment: __REPLACE_ME_ENVIRONMENT__
- Region: __REPLACE_ME_REGION__
- Cluster: __REPLACE_ME_CLUSTER_NAME__
- Owner: __REPLACE_ME_OWNER__
## 구성
`kustomization.yaml``resources`에 필요한 platform, policy, tenant와 app의
base 또는 overlay를 추가합니다. 예:
```yaml
resources:
- ../../../platform/core/base
- ../../../policies/baseline/base
- ../../../apps/example-api/overlays/prod
```
위 예시는 `clusters/dev/main` 경로를 기준으로 합니다. 실제 상대 경로는 cluster
leaf 깊이에 맞게 조정합니다.
## 규칙
- 이 디렉터리가 해당 클러스터의 유일한 root입니다.
- 공통 manifest를 복사하지 않습니다.
- 클러스터 고유 차이만 local patch로 둡니다.
- dependency/sync 순서와 장애 시 reconcile 중지 절차를 문서화합니다.
- `_template` 자체를 GitOps controller에 연결하지 않습니다.
@@ -0,0 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Add reusable platform, policy, tenant and app paths after copying this template.
resources: []
+22
View File
@@ -0,0 +1,22 @@
# lab / main cluster entrypoints
현재 `lab`은 readiness 경계를 가진 push-based staged deployment를 사용합니다.
```text
namespaces
stages/
├── 00-platform
├── 10-vault
├── 20-secrets
├── 30-data
├── 35-registry
├── 40-operations
└── 50-apps
```
- `namespaces`와 각 stage는 독립적으로 렌더 가능한 cluster 배포 entrypoint입니다.
- `scripts/bin/bootstrap.sh lab`만 준비 상태를 확인하며 순차 적용합니다.
- `all/`은 모든 stage를 합친 감사용 Kustomization입니다. apply하거나 GitOps
controller root로 연결하지 않습니다.
- Argo CD를 채택하면 stage별 Application/sync-wave/health gate를, Flux를
채택하면 Kustomization `dependsOn`/`healthChecks`를 먼저 설계한 뒤 전환합니다.
@@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Audit/render entrypoint only. Do not apply this aggregate: controllers and
# one-shot operations have readiness boundaries that Kustomize cannot express.
resources:
- ../stages/00-platform
- ../stages/10-vault
- ../stages/20-secrets
- ../stages/30-data
- ../stages/35-registry
- ../stages/40-operations
- ../stages/50-apps
@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Namespace creation is a separate cluster entrypoint because server-side
# dry-run cannot create a Namespace and validate namespaced resources against
# it in the same request.
resources:
- ../../../../tenants/mnt/base
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Cluster prerequisites and the deny-by-default policy are applied before any
# workload. This stage is intentionally free of application CRs.
resources:
- ../../../../../tenants/mnt/base
- ../../../../../platform/cert-manager/overlays/lab
- ../../../../../platform/keycloak-operator/overlays/lab
- ../../../../../platform/traefik/overlays/lab
- ../../../../../policies/baseline/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../../../platform/vault/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,16 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# The VSO controller is installed by the bootstrap task before this stage.
resources:
- ../../../../../platform/secret-delivery/overlays/lab/operator
- ../../../../../platform/secret-delivery/overlays/lab/identity
- ../../../../../platform/secret-delivery/overlays/lab/storage
- ../../../../../platform/secret-delivery/overlays/lab/apps
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../../../apps/identity-postgres/overlays/lab
- ../../../../../platform/minio/overlays/lab
- ../../../../../platform/keycloak/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../../../platform/secret-delivery/overlays/lab/storage/registry-minio
- ../../../../../platform/registry/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# One-shot resources. bootstrap.sh waits for their prerequisites before apply.
resources:
- ../../../../../apps/auth-migration/overlays/lab
- ../../../../../apps/keycloak-realm-import/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../../../../apps/auth-server/overlays/lab
- ../../../../../platform/forward-auth/overlays/lab
labels:
- pairs:
example.com/environment: lab
example.com/owner-team: platform
includeSelectors: false
includeTemplates: true
+6
View File
@@ -0,0 +1,6 @@
# gitops/platform AGENTS
Shared platform services and environment-neutral operator sources live in
unit-level `base` directories. Cluster-specific CIDRs, namespaces, ingress,
service mesh, load balancer, and K3s packaged-component configuration belong
in focused overlays selected by a cluster stage.
+23
View File
@@ -0,0 +1,23 @@
# Platform Catalog
클러스터 전체에서 사용하는 addon과 controller의 재사용 구성을 둡니다.
예:
```text
platform/
├── ingress/
├── certificates/
├── external-dns/
├── external-secrets/
├── storage/
├── autoscaling/
└── observability/
```
실제로 사용하는 항목만 만듭니다. 각 component는 자신의 CRD, controller,
configuration과 values/patch를 응집해서 관리합니다. component 간 숨은 의존을
만들지 말고 cluster root 또는 선택한 GitOps controller의 ordering 기능으로
순서를 표현합니다.
새 component는 `_template`을 복사해 시작합니다.
+21
View File
@@ -0,0 +1,21 @@
# __REPLACE_ME_PLATFORM_COMPONENT_NAME__
## 책임
이 component가 소유하는 CRD, controller와 configuration을 적습니다.
## 의존성
선행 component, namespace, identity와 최소 Kubernetes version을 적습니다.
## 구성
- `base`: 모든 대상에서 공유하는 기본값
- `overlays`: 환경/규모별 차이가 실제로 있을 때만 추가
Helm을 사용하면 chart source/version, values와 release 리소스를 이 component
안에 함께 둡니다. 원격 version은 고정합니다.
## 운영
upgrade 순서, health check, rollback과 uninstall 영향을 runbook으로 연결합니다.
+7
View File
@@ -0,0 +1,7 @@
# Base
환경과 클러스터를 모르는 재사용 가능한 기본 manifest만 둡니다.
- 실제 domain, account ID, credential을 하드코딩하지 않습니다.
- resource request/limit와 security context의 안전한 기본값을 둡니다.
- 환경 차이는 `overlays`에서 patch합니다.
@@ -0,0 +1,4 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: []
@@ -0,0 +1,7 @@
# Overlays
공통 base와 다른 값이 있을 때만 `<environment-or-profile>/` overlay를 만듭니다.
overlay는 base를 참조하고 patch만 포함해야 합니다.
`dev`, `prod` 전체 manifest 복사보다 replica, resource, retention처럼 실제로
달라지는 항목만 표현합니다.
@@ -0,0 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.yaml
@@ -0,0 +1,95 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
labels:
- pairs:
app.kubernetes.io/part-of: certificate-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
patches:
- target:
kind: Deployment
name: cert-manager
patch: |-
- op: add
path: /spec/revisionHistoryLimit
value: 5
- op: add
path: /spec/progressDeadlineSeconds
value: 600
- op: add
path: /spec/template/spec/securityContext/runAsUser
value: 1000
- op: add
path: /spec/template/spec/securityContext/runAsGroup
value: 1000
- op: add
path: /spec/template/spec/securityContext/fsGroup
value: 1000
- op: add
path: /spec/template/spec/containers/0/resources
value:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 256Mi
- target:
kind: Deployment
name: cert-manager-cainjector
patch: |-
- op: add
path: /spec/revisionHistoryLimit
value: 5
- op: add
path: /spec/progressDeadlineSeconds
value: 600
- op: add
path: /spec/template/spec/securityContext/runAsUser
value: 1000
- op: add
path: /spec/template/spec/securityContext/runAsGroup
value: 1000
- op: add
path: /spec/template/spec/securityContext/fsGroup
value: 1000
- op: add
path: /spec/template/spec/containers/0/resources
value:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 256Mi
- target:
kind: Deployment
name: cert-manager-webhook
patch: |-
- op: add
path: /spec/revisionHistoryLimit
value: 5
- op: add
path: /spec/progressDeadlineSeconds
value: 600
- op: add
path: /spec/template/spec/securityContext/runAsUser
value: 1000
- op: add
path: /spec/template/spec/securityContext/runAsGroup
value: 1000
- op: add
path: /spec/template/spec/securityContext/fsGroup
value: 1000
- op: add
path: /spec/template/spec/containers/0/resources
value:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 128Mi
@@ -0,0 +1,115 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: oauth2-proxy
spec:
replicas: 1
revisionHistoryLimit: 5
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
template:
metadata:
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
spec:
serviceAccountName: oauth2-proxy-sa
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
imagePullPolicy: IfNotPresent
args:
- --config=/etc/oauth2-proxy/oauth2-proxy.cfg
ports:
- name: http
containerPort: 4180
protocol: TCP
- name: metrics
containerPort: 44180
protocol: TCP
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
startupProbe:
httpGet:
path: /ping
port: http
periodSeconds: 5
failureThreshold: 12
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: http
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /ping
port: http
periodSeconds: 15
failureThreshold: 3
timeoutSeconds: 3
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: config
mountPath: /etc/oauth2-proxy
readOnly: true
- name: secrets
mountPath: /etc/oauth2-proxy-secrets
readOnly: true
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: oauth2-proxy-config
- name: secrets
secret:
secretName: oauth2-proxy-secrets
defaultMode: 0400
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 64Mi
@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- service.yaml
- deployment.yaml
labels:
- pairs:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/version: "7.15.2"
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: oauth2-proxy
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
ports:
- name: http
port: 4180
targetPort: http
protocol: TCP
appProtocol: http
- name: metrics
port: 44180
targetPort: metrics
protocol: TCP
appProtocol: http
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: oauth2-proxy-sa
automountServiceAccountToken: false
@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- ../base
- oauth2-proxy-config.yaml
- oauth2-proxy-ingress.yaml
- oauth2-proxy-middleware.yaml
- oauth2-proxy-networkpolicy.yaml
patches:
- target:
kind: Ingress
name: auth-server
patch: |-
- op: replace
path: /metadata/annotations/traefik.ingress.kubernetes.io~1router.middlewares
value: mnt-oauth2-proxy-errors@kubernetescrd,mnt-oauth2-proxy-auth@kubernetescrd,kube-system-security-headers@kubernetescrd
@@ -0,0 +1,46 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: oauth2-proxy-config
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/version: "7.15.2"
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
data:
oauth2-proxy.cfg: |
provider = "keycloak-oidc"
http_address = "0.0.0.0:4180"
metrics_address = "0.0.0.0:44180"
redirect_url = "http://auth.local.test/oauth2/callback"
oidc_issuer_url = "http://keycloak.local.test/realms/platform"
skip_oidc_discovery = true
login_url = "http://keycloak.local.test/realms/platform/protocol/openid-connect/auth"
redeem_url = "http://keycloak.mnt.svc.cluster.local/realms/platform/protocol/openid-connect/token"
profile_url = "http://keycloak.mnt.svc.cluster.local/realms/platform/protocol/openid-connect/userinfo"
validate_url = "http://keycloak.mnt.svc.cluster.local/realms/platform/protocol/openid-connect/userinfo"
oidc_jwks_url = "http://keycloak.mnt.svc.cluster.local/realms/platform/protocol/openid-connect/certs"
client_id = "auth-server-ingress"
client_secret_file = "/etc/oauth2-proxy-secrets/client-secret"
cookie_secret_file = "/etc/oauth2-proxy-secrets/cookie-secret"
reverse_proxy = true
upstreams = [ "static://202" ]
email_domains = [ "*" ]
scope = "openid profile email"
insecure_oidc_allow_unverified_email = true
skip_provider_button = true
cookie_secure = false
cookie_samesite = "lax"
cookie_csrf_per_request = true
cookie_refresh = "4m"
set_xauthrequest = true
set_authorization_header = true
pass_access_token = true
pass_authorization_header = true
ssl_insecure_skip_verify = false
whitelist_domains = [ "auth.local.test" ]
trusted_proxy_ips = [ "10.42.0.0/16" ]
@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oauth2-proxy
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/version: "7.15.2"
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: kube-system-security-headers@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: auth.local.test
http:
paths:
- path: /oauth2/
pathType: Prefix
backend:
service:
name: oauth2-proxy
port:
name: http
@@ -0,0 +1,45 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth2-proxy-auth
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/version: "7.15.2"
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
forwardAuth:
address: http://oauth2-proxy.mnt.svc.cluster.local:4180/oauth2/auth
trustForwardHeader: true
authResponseHeaders:
- Authorization
- X-Auth-Request-Access-Token
- X-Auth-Request-Email
- X-Auth-Request-Preferred-Username
- X-Auth-Request-User
- X-Forwarded-Email
- X-Forwarded-User
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth2-proxy-errors
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/version: "7.15.2"
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
errors:
status:
- "401"
statusRewrites:
"401": 302
service:
name: oauth2-proxy
port: 4180
query: /oauth2/start?rd={url}
@@ -0,0 +1,55 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: oauth2-proxy-ingress-traefik
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- protocol: TCP
port: 4180
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: oauth2-proxy-egress-keycloak
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
ports:
- protocol: TCP
port: 8080
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
components:
- ../../component
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.6.1/kubernetes/keycloaks.k8s.keycloak.org-v1.yml
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.6.1/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml
- https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/26.6.1/kubernetes/kubernetes.yml
@@ -0,0 +1,67 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- networkpolicy.yaml
labels:
- pairs:
app.kubernetes.io/instance: keycloak-operator
app.kubernetes.io/component: identity-operator
app.kubernetes.io/part-of: auth-platform
includeSelectors: false
includeTemplates: true
patches:
- target:
kind: ClusterRoleBinding
name: keycloak-operator-clusterrole-binding
patch: |-
- op: replace
path: /subjects/0/namespace
value: mnt
- target:
kind: Deployment
name: keycloak-operator
patch: |-
- op: add
path: /spec/revisionHistoryLimit
value: 5
- op: add
path: /spec/progressDeadlineSeconds
value: 600
- op: add
path: /spec/template/spec/securityContext
value:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
- op: add
path: /spec/template/spec/containers/0/securityContext
value:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
- op: add
path: /spec/template/spec/containers/0/volumeMounts
value:
- name: tmp
mountPath: /tmp
- op: add
path: /spec/template/spec/volumes
value:
- name: tmp
emptyDir:
sizeLimit: 128Mi
- op: replace
path: /spec/template/spec/containers/0/startupProbe/failureThreshold
value: 18
@@ -0,0 +1,29 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-operator-egress-kubernetes-api
labels:
app.kubernetes.io/name: keycloak-operator
app.kubernetes.io/instance: keycloak-operator
app.kubernetes.io/component: identity-operator
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: keycloak-operator
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.43.0.0/16
ports:
- protocol: TCP
port: 443
- to:
- ipBlock:
cidr: 10.208.141.0/24
ports:
- protocol: TCP
port: 6443
+113
View File
@@ -0,0 +1,113 @@
apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
name: keycloak
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/version: "26.6.1"
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: keycloak-operator
spec:
instances: 2
db:
vendor: postgres
host: identity-postgres
port: 5432
database: keycloak
usernameSecret:
name: keycloak-db-operator
key: username
passwordSecret:
name: keycloak-db-operator
key: password
poolInitialSize: 5
poolMinSize: 5
poolMaxSize: 20
bootstrapAdmin:
user:
secret: keycloak-bootstrap-admin-operator # 어떤 시크릿에서 읽을지 지정하는 거
hostname:
strict: true # 명시된 hostname을 기준으로 URL을 만든다.
backchannelDynamic: false # 백URL도 프론트 hostname과 동일하게 고정한다.
http:
httpEnabled: true
httpPort: 8080
serviceHttpPort: 80
serviceName: keycloak
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: keycloak-operator
proxy:
headers: xforwarded # X-Forwarded-For X-Forwarded-Proto X-Forwarded-Host X-Forwarded-Port 해당 헤더들을 사용하겠다는 의미
ingress:
enabled: false
networkPolicy:
enabled: false
additionalOptions:
- name: cache
value: ispn # 애플리케이션의 캐시 엔진으로 ispn으로 사용
- name: cache-stack
value: jdbc-ping # 클러스터링된 서버끼리 서로를 찾는(Discovery) 방식을 JDBC-PING으로 정한 것
- name: health-enabled
value: "true" # 상태 확인(Health Check) 엔드포인트를 활성화
- name: metrics-enabled
value: "true" # 메트릭(성능 지표) 수집 기능을 켬
- name: log-console-output
value: json # 로그 형식을 JSON 구조로 변경
env:
- name: JAVA_OPTS_APPEND
value: "-XX:MaxRAMPercentage=70 -XX:InitialRAMPercentage=50 -XX:+ExitOnOutOfMemoryError"
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 2Gi
scheduling:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
affinity: # 특정 노드에 다른 pod에 관계에 따라 배치 위치를 결정하는 규칙을 정의한다
podAntiAffinity: # 특정 조건을 만족하는 포드와는 같은 장소에 있지 않는다.
preferredDuringSchedulingIgnoredDuringExecution: # 가급적 지켜줘
- weight: 100 # 가중치가 100임 매우 중요하다고 알림
podAffinityTerm: # 어떤 파드를 피해다닐지
topologyKey: kubernetes.io/hostname # 피할 기준은 호스트 네임으로 설정
labelSelector:
matchLabels:
app.kubernetes.io/instance: keycloak # keycloak과 피하겠다
app.kubernetes.io/managed-by: keycloak-operator # keycloak-operator에 의해 관리되는
unsupported:
podTemplate:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containers:
- name: keycloak
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- keycloak.yaml
labels:
- pairs:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/version: "26.6.1"
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-admin
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/version: "26.6.1"
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: mnt-keycloak-security-headers@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: keycloak-admin.local.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: keycloak
port:
name: http
@@ -0,0 +1,48 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: keycloak-public
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/version: "26.6.1"
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: mnt-keycloak-security-headers@kubernetescrd # frameDeny 제외 — admin 콘솔의 silent SSO iframe 허용
spec:
ingressClassName: traefik
rules:
- host: keycloak.local.test
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
- path: /js/
pathType: Prefix
backend:
service:
name: keycloak
port:
name: http
@@ -0,0 +1,8 @@
apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
name: keycloak
spec:
hostname:
hostname: http://keycloak.local.test
admin: http://keycloak-admin.local.test
@@ -0,0 +1,17 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- middleware.yaml
- ingress-public.yaml
- ingress-admin.yaml
- networkpolicy.yaml
patches:
- target:
kind: Keycloak
name: keycloak
path: keycloak-hostname-patch.yaml
@@ -0,0 +1,15 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: keycloak-security-headers
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
headers:
browserXssFilter: true
contentTypeNosniff: true
referrerPolicy: strict-origin-when-cross-origin
@@ -0,0 +1,140 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-peer
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-ingress-traefik
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-egress-postgres
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: identity-postgres
app.kubernetes.io/instance: identity-postgres
ports:
- protocol: TCP
port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-ingress-auth-server
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: keycloak-ingress-oauth2-proxy
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/instance: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/instance: keycloak
app.kubernetes.io/managed-by: keycloak-operator
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/instance: oauth2-proxy
ports:
- protocol: TCP
port: 8080
@@ -0,0 +1,14 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- tenant.yaml
labels:
- pairs:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/version: "2025.01.20"
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
+92
View File
@@ -0,0 +1,92 @@
apiVersion: minio.min.io/v2
kind: Tenant
metadata:
name: minio
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/version: "2025.01.20"
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
image: minio/minio:RELEASE.2025-01-20T14-49-07Z
imagePullPolicy: IfNotPresent
mountPath: /export
configuration:
name: minio-tenant-env
requestAutoCert: true
certConfig:
commonName: minio
organizationName:
- example.com
dnsNames:
- minio
- minio-hl
pools:
- name: pool-0
servers: 4
volumesPerServer: 4
volumeClaimTemplate:
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: replace-in-overlay
resources:
requests:
cpu: 250m
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
fsGroup: 1000
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containerSecurityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
features:
bucketDNS: false
prometheusOperator: false
podManagementPolicy: Parallel
exposeServices:
minio: false
console: false
logging:
anonymous: false
json: true
quiet: false
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: minio-pdb
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/version: "2025.01.20"
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
maxUnavailable: 1
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
v1.min.io/tenant: minio
@@ -0,0 +1,32 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- networkpolicy.yaml
patches:
- target:
group: minio.min.io
version: v2
kind: Tenant
name: minio
patch: |-
- op: replace
path: /spec/certConfig/dnsNames
value:
- minio
- minio-hl
- minio.mnt.svc.cluster.local
- "*.minio-hl.mnt.svc.cluster.local"
- op: replace
path: /spec/requestAutoCert
value: false
- op: replace
path: /spec/pools/0/volumesPerServer
value: 1
- op: replace
path: /spec/pools/0/volumeClaimTemplate/spec/storageClassName
value: local-path
@@ -0,0 +1,140 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: minio-ingress-peer
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
v1.min.io/tenant: minio
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
v1.min.io/tenant: minio
ports:
- protocol: TCP
port: 9000
- protocol: TCP
port: 9001
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: minio-ingress-operator
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
v1.min.io/tenant: minio
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: minio-operator
podSelector:
matchLabels:
app.kubernetes.io/instance: minio-operator
app.kubernetes.io/name: operator
ports:
- protocol: TCP
port: 9000
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: minio-ingress-clients
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
v1.min.io/tenant: minio
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/part-of: auth-platform
- podSelector:
matchLabels:
app.kubernetes.io/part-of: platform-registry
ports:
- protocol: TCP
port: 9000
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: minio-egress-peer
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
v1.min.io/tenant: minio
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
v1.min.io/tenant: minio
ports:
- protocol: TCP
port: 9000
- protocol: TCP
port: 9001
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: minio-egress-kubernetes-api
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/instance: minio
app.kubernetes.io/component: object-storage
app.kubernetes.io/part-of: storage-platform
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
v1.min.io/tenant: minio
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.43.0.0/16
ports:
- protocol: TCP
port: 443
- to:
- ipBlock:
cidr: 10.208.141.0/24
ports:
- protocol: TCP
port: 6443
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-registry-config
data:
REGISTRY_HTTP_ADDR: "0.0.0.0:5000"
REGISTRY_LOG_FORMATTER: "json"
REGISTRY_STORAGE: "s3"
REGISTRY_STORAGE_S3_REGION: "us-east-1"
REGISTRY_STORAGE_S3_REGIONENDPOINT: "https://minio"
REGISTRY_STORAGE_S3_BUCKET: "docker-registry"
REGISTRY_STORAGE_S3_FORCEPATHSTYLE: "true"
REGISTRY_STORAGE_S3_SKIPVERIFY: "true"
REGISTRY_STORAGE_REDIRECT_DISABLE: "true"
REGISTRY_STORAGE_DELETE_ENABLED: "true"
@@ -0,0 +1,94 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry
spec:
replicas: 1
revisionHistoryLimit: 5
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
selector:
matchLabels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
template:
metadata:
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
spec:
serviceAccountName: docker-registry
automountServiceAccountToken: false
terminationGracePeriodSeconds: 45
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
containers:
- name: registry
image: registry:2.8.3
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 5000
protocol: TCP
envFrom:
- configMapRef:
name: docker-registry-config
- secretRef:
name: docker-registry-minio
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
startupProbe:
httpGet:
path: /v2/
port: http
periodSeconds: 5
failureThreshold: 12
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /v2/
port: http
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /v2/
port: http
periodSeconds: 30
failureThreshold: 3
timeoutSeconds: 3
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 64Mi
+19
View File
@@ -0,0 +1,19 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- configmap.yaml
- service.yaml
- deployment.yaml
labels:
- pairs:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/version: "2.8.3"
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: docker-registry
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
ports:
- name: http
port: 5000
targetPort: http
protocol: TCP
appProtocol: http
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: docker-registry
automountServiceAccountToken: false
@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docker-registry-public
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/version: "2.8.3"
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: kube-system-security-headers@kubernetescrd,mnt-docker-registry-basic-auth@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: registry.project.com
http:
paths:
- path: /v2
pathType: Prefix
backend:
service:
name: docker-registry
port:
name: http
@@ -0,0 +1,22 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- ../../base
- middleware.yaml
- ingress-public.yaml
- networkpolicy.yaml
patches:
- target:
kind: ConfigMap
name: docker-registry-config
patch: |-
- op: replace
path: /data/REGISTRY_STORAGE_S3_REGIONENDPOINT
value: http://minio
- op: replace
path: /data/REGISTRY_STORAGE_S3_SKIPVERIFY
value: "false"
@@ -0,0 +1,14 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: docker-registry-basic-auth
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
spec:
basicAuth:
secret: docker-registry-basic-auth
realm: docker-registry
@@ -0,0 +1,82 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: docker-registry-ingress-intra
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
ports:
- protocol: TCP
port: 5000
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: docker-registry-ingress-traefik
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ports:
- protocol: TCP
port: 5000
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: docker-registry-egress-minio
labels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
app.kubernetes.io/component: registry
app.kubernetes.io/part-of: platform-registry
app.kubernetes.io/managed-by: kustomize
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: docker-registry
app.kubernetes.io/instance: docker-registry
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
v1.min.io/tenant: minio
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
- protocol: TCP
port: 9000
+38
View File
@@ -0,0 +1,38 @@
defaultVaultConnection:
enabled: false
controller:
# VSO 는 전용 namespace 에 설치하지만 같은 restricted baseline 을 강제한다.
podSecurityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
manager:
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 10m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
kubeRbacProxy:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
# kubeRBACProxy : metrics 접근 제어
+18
View File
@@ -0,0 +1,18 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- vault-connection.yaml
- vault-auth.yaml
labels:
- pairs:
app.kubernetes.io/name: vault-secrets-operator
app.kubernetes.io/instance: vso
app.kubernetes.io/version: "0.9.0"
app.kubernetes.io/component: secret-delivery
app.kubernetes.io/part-of: security-platform
app.kubernetes.io/managed-by: kustomize
includeSelectors: false
includeTemplates: true
+5
View File
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-secrets-operator
automountServiceAccountToken: true
+27
View File
@@ -0,0 +1,27 @@
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: vault-auth-auth-platform
spec:
vaultConnectionRef: vault-connection
method: kubernetes
mount: kubernetes
kubernetes:
role: vso-auth-platform
serviceAccount: vault-secrets-operator
audiences:
- vault
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: vault-auth-storage
spec:
vaultConnectionRef: vault-connection
method: kubernetes
mount: kubernetes
kubernetes:
role: vso-storage
serviceAccount: vault-secrets-operator
audiences:
- vault
@@ -0,0 +1,7 @@
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
name: vault-connection
spec:
address: http://vault.mnt.svc.cluster.local:8200
skipTLSVerify: false
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mnt
resources:
- oauth2-proxy.yaml
@@ -0,0 +1,28 @@
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: oauth2-proxy-secrets
spec:
vaultAuthRef: vault-auth-auth-platform
mount: secret
path: oauth2-proxy/forward-auth
refreshAfter: 60s
type: kv-v2
destination:
name: oauth2-proxy-secrets
create: true
overwrite: true
labels:
app.kubernetes.io/name: oauth2-proxy
app.kubernetes.io/component: auth-proxy
app.kubernetes.io/managed-by: vault-secrets-operator
transformation:
excludeRaw: true
templates:
client-secret:
text: '{{ if index .Secrets "client_secret" }}{{ index .Secrets "client_secret" }}{{ else }}{{ index .Secrets "client-secret" }}{{ end }}'
cookie-secret:
text: '{{ if index .Secrets "cookie_secret" }}{{ index .Secrets "cookie_secret" }}{{ else }}{{ index .Secrets "cookie-secret" }}{{ end }}'
rolloutRestartTargets:
- kind: Deployment
name: oauth2-proxy
@@ -0,0 +1,94 @@
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: keycloak-bootstrap-admin
spec:
vaultAuthRef: vault-auth-auth-platform
mount: secret
type: kv-v2
path: keycloak/bootstrap-admin
refreshAfter: 60s
destination:
name: keycloak-bootstrap-admin
create: true
overwrite: true
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/managed-by: vault-secrets-operator
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: keycloak-bootstrap-admin-operator
spec:
vaultAuthRef: vault-auth-auth-platform
mount: secret
type: kv-v2
path: keycloak/bootstrap-admin
refreshAfter: 60s
destination:
name: keycloak-bootstrap-admin-operator
create: true
overwrite: true
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/managed-by: vault-secrets-operator
transformation:
excludeRaw: true
templates:
username:
text: '{{ .Secrets.KEYCLOAK_ADMIN }}'
password:
text: '{{ .Secrets.KEYCLOAK_ADMIN_PASSWORD }}'
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: keycloak-db-operator
spec:
vaultAuthRef: vault-auth-auth-platform
mount: secret
type: kv-v2
path: keycloak/db
refreshAfter: 60s
destination:
name: keycloak-db-operator
create: true
overwrite: true
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/component: database
app.kubernetes.io/managed-by: vault-secrets-operator
transformation:
excludeRaw: true
templates:
username:
text: keycloak
password:
text: '{{ .Secrets.password }}'
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: keycloak-client-auth-server-ingress
spec:
vaultAuthRef: vault-auth-auth-platform
mount: secret
type: kv-v2
path: keycloak/clients/auth-server-ingress
refreshAfter: 60s
destination:
name: keycloak-client-auth-server-ingress
create: true
overwrite: true
labels:
app.kubernetes.io/name: keycloak
app.kubernetes.io/component: identity-provider
app.kubernetes.io/managed-by: vault-secrets-operator
transformation:
excludeRaw: true
templates:
client-secret:
text: '{{ .Secrets.client_secret }}'

Some files were not shown because too many files have changed in this diff Show More