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-datanamespace를 먼저 적용한다.- 이 서비스를 적용하기 전에
/srv/k3s/ssd/gitea를 준비하고gitea-local-pv기반 구성을 적용한다.gitea-dataClaim은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 전체 페이지 템플릿은 복제하거나 수정하지 않는다.
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는 검증된 변환 절차를 추가하기 전까지 업스트림 기본 자산을 사용한다.
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를 확인하는 검증기로 렌더링한다.
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 계약이 준비된 뒤 다음 전용 진입점으로 적용한다.
- Host Nginx의
id.learn.hyeonworks.com정적 hold를 Keycloak proxy 후보로 전환한다. - public discovery JSON의
issuer가https://id.learn.hyeonworks.com/realms/hyeonworks와 정확히 같은지 확인한다. - 다음 전용 스크립트로 OIDC 목표 상태와 브랜딩을 함께 적용한다.
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 상태를 적용하거나
복구하지 않는다.