Add platform infrastructure configuration
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
# Gitea 서비스
|
||||
|
||||
Gitea는 같은 공식 Helm Chart와 공통 values를 사용하는 두 Kustomize 빌드 루트로
|
||||
관리한다. 업스트림 Chart 템플릿은 복제하거나 수정하지 않는다.
|
||||
|
||||
| 용도 | 빌드 루트 | 검증 산출물 | 적용 진입점 |
|
||||
| --- | --- | --- | --- |
|
||||
| 신규 설치 baseline | `services/gitea` | `gitea.yaml` | `apply-phase1-gitea.sh` |
|
||||
| OIDC·브랜딩 목표 상태 | `services/gitea/profiles/oidc` | `gitea-oidc.yaml` | `apply-gitea-oidc.sh` |
|
||||
|
||||
baseline 루트는 OIDC Secret, `id` host alias, Keycloak 전용 egress와 브랜딩을
|
||||
포함하지 않는다. OIDC profile만 공통 기반에 이 네 요소를 추가한다.
|
||||
|
||||
상태(2026-07-23): Host Nginx public discovery 전환과 OIDC-enabled Gitea
|
||||
rollout을 완료했다. 적용 manifest SHA-256은 `d25a757...a157`이며, 활성
|
||||
`keycloak` OAuth2 source, 외부 인증 전용 가입 정책, authorization-code redirect와
|
||||
아래 선언형 브랜딩의 자동 검증을 모두 통과했다. 실제 realm 사용자의 브라우저
|
||||
login/callback/logout, 비상 관리자 실제 로그인과 Pod 재시작 뒤 설정 지속성은
|
||||
별도 수용 시험으로 남아 있다.
|
||||
|
||||
## 고정된 업스트림 릴리스
|
||||
|
||||
| 항목 | 값 |
|
||||
| --- | --- |
|
||||
| Chart 저장소 | `https://dl.gitea.com/charts/` |
|
||||
| Chart | `gitea` |
|
||||
| Chart 버전 | `12.7.0` |
|
||||
| Chart 패키지 SHA-256 | `5881ef9c59400bee2d5547e77c4cd0efb925143c2f5d93fb4f38446db76b0167` |
|
||||
| Gitea 이미지 | `docker.gitea.com/gitea:1.27.0-rootless` |
|
||||
|
||||
Chart는 렌더링할 때 저장소 공통 `.gitignore`에서 제외한
|
||||
`profiles/oidc/.helm/charts`에 내려받는다. baseline 루트와 OIDC profile이 이 고정
|
||||
SHA 검증 캐시 하나를 각자의 `chartHome`으로 참조한다. values와 Patch는 모두
|
||||
`services/gitea` 아래에 있으므로 Kustomize의 기본 `RootOnly` 로드 제한을 유지한다.
|
||||
|
||||
## baseline과 OIDC 로그인 정책
|
||||
|
||||
두 프로필이 공유하는 `profiles/oidc/values/baseline.yaml`은 다음 정책을 선언한다.
|
||||
|
||||
- 자체 가입을 닫고(`DISABLE_REGISTRATION=true`) 외부 인증 전용 가입도 끈다.
|
||||
- 가입 버튼을 숨기고 기존 `gitea-admin` 로컬 비상 로그인을 유지한다.
|
||||
- PostgreSQL, PVC, probe, resource와 restricted securityContext를 공통으로 둔다.
|
||||
|
||||
`profiles/oidc/values/oidc.yaml`은 baseline 위에 다음 항목만 덮어쓴다.
|
||||
|
||||
- `APP_NAME=Hyeonworks Git`으로 브라우저 제목과 애플리케이션 이름을 고정한다.
|
||||
- `DISABLE_REGISTRATION=false`와 `ALLOW_ONLY_EXTERNAL_REGISTRATION=true`를 함께 사용해
|
||||
Keycloak 같은 외부 인증을 통한 신규 사용자 생성만 허용한다.
|
||||
- `SHOW_REGISTRATION_BUTTON=false`로 Gitea 자체 공개 가입 버튼을 숨긴다.
|
||||
- `oauth2_client.ENABLE_AUTO_REGISTRATION=true`, `USERNAME=preferred_username`,
|
||||
`OPENID_CONNECT_SCOPES="profile email"`로 OIDC 최초 로그인 계정을 생성한다.
|
||||
- baseline의 `ENABLE_PASSWORD_SIGNIN_FORM=true`를 유지해 Keycloak 장애 시에도 로컬
|
||||
비상 관리자가 로그인할 수 있게 한다.
|
||||
|
||||
`ACCOUNT_LINKING=login`은 이메일이 같은 기존 계정을 자동 병합하지 않는다. 사용자가
|
||||
기존 Gitea 자격 증명으로 한 번 더 확인해야 연결되므로, 이메일 일치만으로 계정을
|
||||
탈취하는 위험을 줄인다.
|
||||
|
||||
## 의존성과 트래픽
|
||||
|
||||
- `gitea`와 `platform-data` namespace를 먼저 적용한다.
|
||||
- 이 서비스를 적용하기 전에 `/srv/k3s/ssd/gitea`를 준비하고 `gitea-local-pv`
|
||||
기반 구성을 적용한다. `gitea-data` Claim은 `ssd-local-gitea-retain`을 통해 해당
|
||||
PV에 바인딩되며 `50Gi`를 요청한다.
|
||||
- 외부 Database endpoint는
|
||||
`platform-postgres-rw.platform-data.svc.cluster.local:5432`다.
|
||||
- Host Nginx에서 공개 TLS를 종료한다. Traefik은 HTTP를 받아 `gitea-http:3000`으로
|
||||
전달하며, 이 Ingress에는 의도적으로 TLS 섹션이 없다.
|
||||
- OIDC profile의 Gitea 서버가 discovery와 token endpoint에 접근할 때도 공개 issuer
|
||||
`https://id.learn.hyeonworks.com/realms/hyeonworks`를 사용한다. 홈 라우터의 NAT
|
||||
loopback에 의존하지 않도록 Pod의 `hostAliases`는 이 이름만 노드
|
||||
`192.168.0.107`로 해석하고, 전용 NetworkPolicy가 해당 `/32`의 TCP 443만
|
||||
허용한다. 연결은 Host Nginx TLS와 Traefik을 그대로 통과한다.
|
||||
- 첫 배포에서는 Gitea SSH, Actions, Git LFS 및 Package Registry를 비활성화한다.
|
||||
Chart `12.7.0`은 항상 SSH Service를 렌더링하므로 baseline 루트는 inline patch,
|
||||
OIDC profile은 `patches/delete-ssh-service.yaml`로 Helm 렌더링 뒤
|
||||
`gitea-ssh`를 삭제한다.
|
||||
- 공통 egress는 클러스터 DNS, 플랫폼 PostgreSQL Pod 및 공개 IPv4 HTTPS 대상으로
|
||||
제한한다. OIDC profile만 Host Nginx 노드 `/32`의 TCP 443을 추가한다. 내부
|
||||
HTTPS, SMTP, SSH remote 및 사설 네트워크 webhook을 사용하려면 향후
|
||||
NetworkPolicy를 명시적으로 변경해야 한다.
|
||||
|
||||
## 선언형 브랜딩
|
||||
|
||||
브랜딩은 Gitea 공식 custom path와 Chart의 volume 확장 필드만 사용한다.
|
||||
Chart 템플릿과 Gitea 전체 페이지 템플릿은 복제하거나 수정하지 않는다.
|
||||
|
||||
```text
|
||||
branding/
|
||||
├── public/assets/
|
||||
│ ├── css/hyeonworks.css
|
||||
│ └── img/
|
||||
│ ├── logo.svg
|
||||
│ └── favicon.svg
|
||||
└── templates/custom/
|
||||
├── header.tmpl
|
||||
└── extra_links.tmpl
|
||||
```
|
||||
|
||||
`branding/kustomization.yaml`의 `configMapGenerator`가 자산과 템플릿 ConfigMap을
|
||||
각각 생성하고 `profiles/oidc/kustomization.yaml`만 이 구성요소를 포함한다. 이름
|
||||
suffix hash를 끄지 않았으므로 파일 내용이 바뀌면 이름도 바뀌고, Kustomize가
|
||||
Deployment의 ConfigMap 참조를 같은 해시 이름으로 치환한다.
|
||||
|
||||
`profiles/oidc/values/oidc.yaml`은 공식 Chart 확장 지점을 다음과 같이 사용한다.
|
||||
|
||||
- 자산 ConfigMap을 `/data/gitea/public/assets`에 마운트한다.
|
||||
- 템플릿 ConfigMap을 `/data/gitea/templates/custom`에 마운트한다.
|
||||
- 두 volume은 rootless Gitea 주 컨테이너에만 `readOnly: true`로 마운트하며,
|
||||
init container에는 추가하지 않는다.
|
||||
|
||||
`header.tmpl`은 같은 origin의 CSS와 모바일 브라우저 색상만 추가한다.
|
||||
`extra_links.tmpl`은 실제 Gitea 내부 경로인 `/explore/organizations`만 연결한다.
|
||||
존재하지 않는 Learning 또는 Identity 서비스 링크는 미리 만들지 않는다.
|
||||
전체 `home.tmpl`은 덮어쓰지 않아 Gitea 업그레이드 때 template drift 범위를 줄인다.
|
||||
Gitea는 custom template을 시작할 때 읽으므로 변경 적용에는 Pod 재시작이 필요하다.
|
||||
|
||||
SVG 원본만 저장소에서 관리한다. Gitea가 참조하는 Open Graph, Apple touch icon 및
|
||||
구형 브라우저용 PNG는 검증된 변환 절차를 추가하기 전까지 업스트림 기본 자산을
|
||||
사용한다.
|
||||
|
||||
- [Gitea 1.27 커스터마이징 공식 문서](https://docs.gitea.com/administration/customizing-gitea)
|
||||
- [Gitea Chart 12.7.0 공식 values](https://gitea.com/gitea/helm-chart/src/tag/v12.7.0/values.yaml)
|
||||
|
||||
## Secret 계약
|
||||
|
||||
credential Secret 매니페스트나 credential 값은 이 저장소에 두지 않는다.
|
||||
`gitea-admin`과 `gitea-db-credentials`는 baseline Deployment 최초 시작 전에,
|
||||
`gitea-keycloak-oidc`는 OIDC-enabled Deployment rollout 전에 `gitea`
|
||||
namespace에 있어야 한다.
|
||||
|
||||
### `gitea-admin`
|
||||
|
||||
| 속성 | 필수 값 |
|
||||
| --- | --- |
|
||||
| Kubernetes 유형 | `kubernetes.io/basic-auth` |
|
||||
| `username` | 최초 로컬 비상 관리자 username |
|
||||
| `password` | 강력한 일회용 관리자 비밀번호 |
|
||||
|
||||
Chart는 `initialOnlyRequireReset`을 사용하므로 최초 로그인 때 초기 비밀번호를
|
||||
변경해야 하며 이후에는 조정하지 않는다.
|
||||
|
||||
### `gitea-db-credentials`
|
||||
|
||||
| 속성 | 필수 값 |
|
||||
| --- | --- |
|
||||
| Kubernetes 유형 | `kubernetes.io/basic-auth` |
|
||||
| `username` | `gitea` |
|
||||
| `password` | PostgreSQL `gitea` Role에 할당한 비밀번호 |
|
||||
|
||||
Kubernetes에서는 namespace를 넘어 Secret을 참조할 수 없다. `platform-data`에 있는
|
||||
같은 이름의 Secret은 CloudNativePG/bootstrap 사본이다. `gitea` namespace의
|
||||
Secret은 동일한 Database credential을 담아야 하며 Chart가 이 사본을 참조한다.
|
||||
|
||||
### `gitea-keycloak-oidc`
|
||||
|
||||
| 속성 | 필수 값 |
|
||||
| --- | --- |
|
||||
| Kubernetes 유형 | `Opaque` |
|
||||
| `key` | `gitea` |
|
||||
| `secret` | Keycloak이 생성한 confidential client secret |
|
||||
|
||||
`scripts/bootstrap/configure-keycloak-gitea-oidc.sh`가 Keycloak Admin REST API로
|
||||
정확한 callback URI를 가진 client를 생성하거나 갱신하고 이 Secret을 만든다.
|
||||
공식 Chart의 `gitea.oauth[].existingSecret`이 `key`와 `secret`을 환경 변수로만
|
||||
초기화 컨테이너에 전달하고, 인증 소스 이름 `keycloak`을 반복 실행 시 갱신한다.
|
||||
Secret payload는 values, 렌더 결과 또는 Git에 포함되지 않는다.
|
||||
|
||||
업스트림 Chart는 credential이 아닌 초기화 스크립트와 inline 설정이 담긴 Opaque
|
||||
Secret도 렌더링한다. 이 생성 리소스들은 고정된 Chart의 일부이며 위 세 credential
|
||||
계약의 값은 포함하지 않는다.
|
||||
|
||||
## 렌더링과 검사
|
||||
|
||||
Helm은 `PATH`에 있어야 하지만 릴리스를 직접 설치하는 데 사용하지 않는다. 플랫폼
|
||||
저장소 루트에서 고정 Chart SHA를 확인하는 검증기로 렌더링한다.
|
||||
|
||||
```sh
|
||||
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
|
||||
bash scripts/validate/render-phase1.sh
|
||||
```
|
||||
|
||||
검증기는 `gitea.yaml` baseline과 `gitea-oidc.yaml` 목표 상태를 모두 렌더한다.
|
||||
두 결과에는 각각 Gitea Deployment 하나, `gitea-http`, `gitea-data`, Traefik
|
||||
Ingress가 있어야 하고 `gitea-ssh`, NodePort, LoadBalancer와 내부 TLS는 없어야
|
||||
한다. 두 Deployment 모두 `gitea-admin`, `gitea-db-credentials`를 참조만 하고
|
||||
생성하지 않는다.
|
||||
|
||||
baseline은 `gitea-keycloak-oidc`, ID host alias, Keycloak 전용 NetworkPolicy,
|
||||
브랜딩 ConfigMap과 외부 인증 전용 가입 설정이 없어야 한다. 반대로 OIDC 산출물은
|
||||
`gitea-keycloak-oidc`를 값 노출 없이 참조하고 다음을 모두 만족해야 한다.
|
||||
|
||||
- 두 브랜딩 ConfigMap 이름에 content hash가 있고 Deployment 참조와 일치한다.
|
||||
- `/data/gitea/public/assets`와 `/data/gitea/templates/custom` 마운트는 read-only다.
|
||||
- inline 설정에 외부 인증 전용 가입, OIDC 자동 계정 생성, `ACCOUNT_LINKING=login`,
|
||||
로컬 비밀번호 로그인 유지가 모두 렌더링된다.
|
||||
- OAuth 인증 소스 이름은 callback URI의 경로와 같은 `keycloak`이고 discovery
|
||||
issuer는 `https://id.learn.hyeonworks.com/realms/hyeonworks`다.
|
||||
|
||||
## Keycloak OIDC 안전 적용
|
||||
|
||||
Host Nginx의 Keycloak discovery가 실제 JSON issuer를 반환하고 위 세 Secret 계약이
|
||||
준비된 뒤 다음 전용 진입점으로 적용한다.
|
||||
|
||||
1. Host Nginx의 `id.learn.hyeonworks.com` 정적 hold를 Keycloak proxy 후보로
|
||||
전환한다.
|
||||
2. public discovery JSON의 `issuer`가
|
||||
`https://id.learn.hyeonworks.com/realms/hyeonworks`와 정확히 같은지 확인한다.
|
||||
3. 다음 전용 스크립트로 OIDC 목표 상태와 브랜딩을 함께 적용한다.
|
||||
|
||||
```sh
|
||||
PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm \
|
||||
bash scripts/bootstrap/apply-gitea-oidc.sh --execute
|
||||
```
|
||||
|
||||
스크립트는 public discovery와 Secret의 type·key 이름만 먼저 확인하고, 고정 Chart
|
||||
SHA 검증 결과 중 `gitea-oidc.yaml` 하나만 적용한다. 이후 Deployment·EndpointSlice,
|
||||
활성 `keycloak` OAuth2 source, 외부 인증 전용 가입 정책, OIDC authorization-code
|
||||
redirect와 브랜딩 자산 SHA-256을 검증한다. Secret 값, bearer token과 OIDC state가
|
||||
포함된 전체 Location은 출력하지 않는다.
|
||||
|
||||
2026-07-23 실제 적용에서는 manifest SHA-256 `d25a757...a157`의 rollout과 위 자동
|
||||
검사를 모두 통과했다. 이 redirect 검사는 realm 사용자의 실제 브라우저
|
||||
login/callback/logout 시험을 대신하지 않는다. self-registration은 비활성이므로
|
||||
named 관리자와 MFA를 준비한 뒤 시험 사용자를 별도로 생성한다. 로컬 비상 관리자
|
||||
실제 로그인과 Pod 재시작 뒤 OAuth source·정책·브랜딩 지속성도 별도로 확인한다.
|
||||
|
||||
기존 Gitea에 `apply-phase1-gitea.sh`를 다시 실행해 OIDC 상태를 적용하거나
|
||||
복구하지 않는다.
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gitea-http
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/component: source-control
|
||||
app.kubernetes.io/part-of: platform
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: web
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: git.learn.hyeonworks.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gitea-http
|
||||
port:
|
||||
number: 3000
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- pvc.yaml
|
||||
- ingress.yaml
|
||||
- network-policies.yaml
|
||||
@@ -0,0 +1,161 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-default-deny
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-traefik-http
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: traefik
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-dns
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
podSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-platform-postgres
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: platform-data
|
||||
podSelector:
|
||||
matchLabels:
|
||||
cnpg.io/cluster: platform-postgres
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-public-git-https
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
- 169.254.0.0/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-prometheus-metrics
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: observability
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: prometheus
|
||||
app.kubernetes.io/instance: observability-core-kube-pr-prometheus
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
@@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: gitea-data
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/component: source-control
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
volumeMode: Filesystem
|
||||
storageClassName: ssd-local-gitea-retain
|
||||
volumeName: gitea-local-pv
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: gitea
|
||||
|
||||
configMapGenerator:
|
||||
- name: gitea-branding-assets
|
||||
files:
|
||||
- hyeonworks.css=public/assets/css/hyeonworks.css
|
||||
- logo.svg=public/assets/img/logo.svg
|
||||
- favicon.svg=public/assets/img/favicon.svg
|
||||
- name: gitea-branding-templates
|
||||
files:
|
||||
- header.tmpl=templates/custom/header.tmpl
|
||||
- extra_links.tmpl=templates/custom/extra_links.tmpl
|
||||
@@ -0,0 +1,59 @@
|
||||
:root {
|
||||
--hyeonworks-accent: #2563eb;
|
||||
--hyeonworks-accent-hover: #1d4ed8;
|
||||
--hyeonworks-focus: rgba(37, 99, 235, 0.35);
|
||||
}
|
||||
|
||||
#navbar-logo {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#navbar-logo img {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
#navbar-logo::after {
|
||||
content: "Hyeonworks Git";
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.015em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#navbar .hw-brand-link {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#navbar .hw-brand-link:hover,
|
||||
#navbar .hw-brand-link:focus-visible {
|
||||
color: var(--hyeonworks-accent);
|
||||
}
|
||||
|
||||
#navbar .hw-brand-link:focus-visible {
|
||||
border-radius: 0.375rem;
|
||||
outline: 3px solid var(--hyeonworks-focus);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.ui.primary.button,
|
||||
.ui.primary.buttons .button {
|
||||
background-color: var(--hyeonworks-accent);
|
||||
}
|
||||
|
||||
.ui.primary.button:hover,
|
||||
.ui.primary.buttons .button:hover {
|
||||
background-color: var(--hyeonworks-accent-hover);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
#navbar-logo::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
#navbar .hw-brand-link,
|
||||
.ui.button {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title">
|
||||
<title id="title">Hyeonworks Git</title>
|
||||
<defs>
|
||||
<linearGradient id="brand-gradient" x1="6" y1="6" x2="58" y2="58" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#38BDF8"/>
|
||||
<stop offset="1" stop-color="#4F46E5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="3" y="3" width="58" height="58" rx="16" fill="url(#brand-gradient)"/>
|
||||
<path d="M19 17v30M45 17v30M19 32h26" fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round"/>
|
||||
<path d="m14 25-6 7 6 7M50 25l6 7-6 7" fill="none" stroke="#BAE6FD" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 695 B |
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title description">
|
||||
<title id="title">Hyeonworks Git</title>
|
||||
<desc id="description">겹쳐진 코드 괄호와 H 문자를 사용한 Hyeonworks Git 로고</desc>
|
||||
<defs>
|
||||
<linearGradient id="brand-gradient" x1="6" y1="6" x2="58" y2="58" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#38BDF8"/>
|
||||
<stop offset="1" stop-color="#4F46E5"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="3" y="3" width="58" height="58" rx="16" fill="url(#brand-gradient)"/>
|
||||
<path d="M19 17v30M45 17v30M19 32h26" fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round"/>
|
||||
<path d="m14 25-6 7 6 7M50 25l6 7-6 7" fill="none" stroke="#BAE6FD" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 810 B |
@@ -0,0 +1 @@
|
||||
<a class="item hw-brand-link" href="{{AppSubUrl}}/explore/organizations">조직</a>
|
||||
@@ -0,0 +1,2 @@
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/hyeonworks.css">
|
||||
@@ -0,0 +1,40 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: gitea
|
||||
|
||||
helmGlobals:
|
||||
chartHome: profiles/oidc/.helm/charts
|
||||
|
||||
helmCharts:
|
||||
- name: gitea
|
||||
repo: https://dl.gitea.com/charts/
|
||||
version: 12.7.0
|
||||
releaseName: gitea
|
||||
namespace: gitea
|
||||
valuesFile: profiles/oidc/values/baseline.yaml
|
||||
includeCRDs: false
|
||||
skipTests: true
|
||||
|
||||
resources:
|
||||
- base
|
||||
|
||||
patches:
|
||||
- path: profiles/oidc/patches/servicemonitor-job-label-patch.yaml
|
||||
target:
|
||||
group: monitoring.coreos.com
|
||||
version: v1
|
||||
kind: ServiceMonitor
|
||||
name: gitea
|
||||
namespace: gitea
|
||||
- target:
|
||||
group: ""
|
||||
version: v1
|
||||
kind: Service
|
||||
name: gitea-ssh
|
||||
patch: |-
|
||||
$patch: delete
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gitea-ssh
|
||||
@@ -0,0 +1,39 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: gitea
|
||||
|
||||
helmGlobals:
|
||||
chartHome: .helm/charts
|
||||
|
||||
helmCharts:
|
||||
- name: gitea
|
||||
repo: https://dl.gitea.com/charts/
|
||||
version: 12.7.0
|
||||
releaseName: gitea
|
||||
namespace: gitea
|
||||
valuesFile: values/baseline.yaml
|
||||
additionalValuesFiles:
|
||||
- values/oidc.yaml
|
||||
includeCRDs: false
|
||||
skipTests: true
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
- ../../branding
|
||||
- network-policy.yaml
|
||||
|
||||
patches:
|
||||
- path: patches/servicemonitor-job-label-patch.yaml
|
||||
target:
|
||||
group: monitoring.coreos.com
|
||||
version: v1
|
||||
kind: ServiceMonitor
|
||||
name: gitea
|
||||
namespace: gitea
|
||||
- path: patches/delete-ssh-service.yaml
|
||||
target:
|
||||
group: ""
|
||||
version: v1
|
||||
kind: Service
|
||||
name: gitea-ssh
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-host-nginx-keycloak
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
app.kubernetes.io/part-of: platform
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
app.kubernetes.io/instance: gitea
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
# CoreDNS split DNS resolves the public issuer name to Host Nginx.
|
||||
cidr: 192.168.0.107/32
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 443
|
||||
@@ -0,0 +1,6 @@
|
||||
$patch: delete
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gitea-ssh
|
||||
namespace: gitea
|
||||
@@ -0,0 +1,7 @@
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: gitea
|
||||
spec:
|
||||
jobLabel: app.kubernetes.io/name
|
||||
@@ -0,0 +1,189 @@
|
||||
namespace: gitea
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
registry: docker.gitea.com
|
||||
repository: gitea
|
||||
tag: "1.27.0"
|
||||
pullPolicy: IfNotPresent
|
||||
rootless: true
|
||||
|
||||
# The official chart applies this context to its built-in init containers,
|
||||
# configure-gitea, and the main container.
|
||||
containerSecurityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
# A Local RWO volume cannot be attached to an old and a replacement Pod at the
|
||||
# same time. Terminate the old Pod before creating its replacement.
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 0
|
||||
maxUnavailable: 1
|
||||
|
||||
service:
|
||||
http:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
clusterIP: ""
|
||||
nodePort: null
|
||||
ssh:
|
||||
type: ClusterIP
|
||||
port: 22
|
||||
clusterIP: ""
|
||||
nodePort: null
|
||||
hostPort: null
|
||||
|
||||
# Routing is owned by ../../base/ingress.yaml. Host Nginx terminates TLS before
|
||||
# forwarding requests to Traefik's HTTP entrypoint.
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
route:
|
||||
enabled: false
|
||||
|
||||
gatewayAPI:
|
||||
enabled: false
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
create: false
|
||||
mount: true
|
||||
claimName: gitea-data
|
||||
|
||||
gitea:
|
||||
admin:
|
||||
existingSecret: gitea-admin
|
||||
email: admin@hyeonworks.com
|
||||
passwordMode: initialOnlyRequireReset
|
||||
|
||||
config:
|
||||
server:
|
||||
PROTOCOL: http
|
||||
DOMAIN: git.learn.hyeonworks.com
|
||||
ROOT_URL: https://git.learn.hyeonworks.com/
|
||||
HTTP_PORT: 3000
|
||||
DISABLE_SSH: true
|
||||
START_SSH_SERVER: false
|
||||
LFS_START_SERVER: false
|
||||
|
||||
# Baseline allows only the pre-provisioned local emergency administrator.
|
||||
# The guarded OIDC profile overrides the first two settings.
|
||||
service:
|
||||
DISABLE_REGISTRATION: true
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION: false
|
||||
SHOW_REGISTRATION_BUTTON: false
|
||||
ENABLE_PASSWORD_SIGNIN_FORM: true
|
||||
|
||||
database:
|
||||
DB_TYPE: postgres
|
||||
HOST: platform-postgres-rw.platform-data.svc.cluster.local:5432
|
||||
NAME: gitea
|
||||
SSL_MODE: disable
|
||||
|
||||
session:
|
||||
PROVIDER: db
|
||||
cache:
|
||||
ADAPTER: memory
|
||||
queue:
|
||||
TYPE: level
|
||||
|
||||
actions:
|
||||
ENABLED: false
|
||||
packages:
|
||||
ENABLED: false
|
||||
repository:
|
||||
DISABLED_REPO_UNITS: "repo.packages,repo.actions"
|
||||
|
||||
additionalConfigFromEnvs:
|
||||
- name: GITEA__DATABASE__USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gitea-db-credentials
|
||||
key: username
|
||||
- name: GITEA__DATABASE__PASSWD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gitea-db-credentials
|
||||
key: password
|
||||
|
||||
metrics:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
additionalLabels:
|
||||
observability.hyeonworks.com/instance: home
|
||||
interval: 30s
|
||||
scrapeTimeout: 10s
|
||||
|
||||
livenessProbe:
|
||||
enabled: true
|
||||
httpGet:
|
||||
path: /api/healthz
|
||||
port: http
|
||||
initialDelaySeconds: 200
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
failureThreshold: 10
|
||||
|
||||
readinessProbe:
|
||||
enabled: true
|
||||
httpGet:
|
||||
path: /api/healthz
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
|
||||
startupProbe:
|
||||
enabled: true
|
||||
httpGet:
|
||||
path: /api/healthz
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
failureThreshold: 60
|
||||
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
postgresql-ha:
|
||||
enabled: false
|
||||
|
||||
valkey:
|
||||
enabled: false
|
||||
|
||||
valkey-cluster:
|
||||
enabled: false
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
|
||||
initContainers:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
test:
|
||||
enabled: false
|
||||
@@ -0,0 +1,49 @@
|
||||
extraVolumes:
|
||||
- name: branding-assets
|
||||
configMap:
|
||||
name: gitea-branding-assets
|
||||
defaultMode: 292
|
||||
items:
|
||||
- key: hyeonworks.css
|
||||
path: css/hyeonworks.css
|
||||
- key: logo.svg
|
||||
path: img/logo.svg
|
||||
- key: favicon.svg
|
||||
path: img/favicon.svg
|
||||
- name: branding-templates
|
||||
configMap:
|
||||
name: gitea-branding-templates
|
||||
defaultMode: 292
|
||||
items:
|
||||
- key: header.tmpl
|
||||
path: header.tmpl
|
||||
- key: extra_links.tmpl
|
||||
path: extra_links.tmpl
|
||||
|
||||
extraContainerVolumeMounts:
|
||||
- name: branding-assets
|
||||
mountPath: /data/gitea/public/assets
|
||||
readOnly: true
|
||||
- name: branding-templates
|
||||
mountPath: /data/gitea/templates/custom
|
||||
readOnly: true
|
||||
|
||||
gitea:
|
||||
oauth:
|
||||
- name: keycloak
|
||||
provider: openidConnect
|
||||
existingSecret: gitea-keycloak-oidc
|
||||
autoDiscoverUrl: https://id.learn.hyeonworks.com/realms/hyeonworks/.well-known/openid-configuration
|
||||
|
||||
config:
|
||||
APP_NAME: Hyeonworks Git
|
||||
|
||||
service:
|
||||
DISABLE_REGISTRATION: false
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION: true
|
||||
|
||||
oauth2_client:
|
||||
ENABLE_AUTO_REGISTRATION: true
|
||||
USERNAME: preferred_username
|
||||
ACCOUNT_LINKING: login
|
||||
OPENID_CONNECT_SCOPES: "profile email"
|
||||
Reference in New Issue
Block a user