Swagger 403, ArgoCD 대시보드, CORS 이슈 해결
2026-03-11 | Swagger 403, ArgoCD 대시보드, CORS 이슈 해결
1. Swagger 403 → OAuth 로그인 리다이렉트#
문제#
swagger.dev.goormgb.space접속 시 403 Forbidden 페이지 표시- 로그인 페이지로 리다이렉트 되지 않음
원인#
- 브라우저에 stale cookie (만료된
_oauth2_proxy쿠키) 존재 - OAuth2 Proxy가 쿠키를 검증하고 “유효하지 않음” → 403 반환
- 정상 흐름: 쿠키 없음 → 302 리다이렉트 → 로그인
[정상 흐름 - 쿠키 없음]
사용자 → OAuth2 Proxy → 쿠키 없음 → 302 → Google 로그인
[문제 상황 - 만료된 쿠키]
사용자 → OAuth2 Proxy → 쿠키 있음 (만료됨) → 403 Forbidden
↑ 여기서 멈춤!
해결: EnvoyFilter로 403→302 리다이렉트#
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: swagger-403-redirect
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
subFilter:
name: envoy.filters.http.router
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inlineCode: |
function envoy_on_response(response_handle)
local host = response_handle:headers():get(":authority") or ""
local status = response_handle:headers():get(":status")
-- swagger 도메인의 403 응답만 처리
if string.find(host, "swagger") and status == "403" then
local path = response_handle:headers():get("x-original-uri") or "/"
local redirect_url = "/oauth2/start?rd=" .. path
-- 302 리다이렉트로 변경
response_handle:headers():replace(":status", "302")
response_handle:headers():add("location", redirect_url)
response_handle:headers():add("cache-control", "no-cache, no-store")
end
end
핵심 포인트#
- envoy_on_response: 응답 단계에서 가로채기
- swagger 도메인만 처리 (다른 서비스 영향 없음)
- 403 → 302 변환 +
/oauth2/start리다이렉트 - OAuth2 Proxy가 다시 로그인 프로세스 시작
2. CORS 에러로 프론트엔드 “하얗게 터짐”#
문제#
- 프론트엔드에서 API 호출 시 CORS 에러
- 브라우저 콘솔에 에러만 보이고 페이지 하얗게 터짐
원인#
- 백엔드가 400, 500 에러 응답에 CORS 헤더를 포함하지 않음
[정상 응답 200]
Access-Control-Allow-Origin: https://dev.goormgb.space ✓
→ 프론트엔드가 응답 본문 읽기 가능
[에러 응답 500]
Access-Control-Allow-Origin: (없음) ✗
→ 브라우저가 응답 차단 → 프론트엔드에서 에러 원인 파악 불가
→ 에러 핸들링 불가 → 하얗게 터짐
왜 에러 응답에도 CORS 헤더가 필요한가?#
- 브라우저 보안 정책: Origin이 다르면 모든 응답에 CORS 헤더 필요
- 에러 핸들링: CORS 헤더 없으면
response.json()자체가 불가능 - UX 개선: 에러 메시지를 사용자에게 보여줄 수 있음
해결 방안 (백엔드 팀 전달)#
// Spring Boot - 에러 응답에도 CORS 헤더 포함
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://dev.goormgb.space")
.allowedMethods("*")
.allowCredentials(true);
}
}
// 또는 @ControllerAdvice에서 모든 예외 응답에 헤더 추가
3. /auth/token/refresh 404 에러#
문제#
- 프론트엔드에서
/auth/token/refresh호출 시 404
원인: 경로 불일치#
| 구분 | 경로 |
|---|---|
| 프론트엔드 호출 | /auth/token/refresh |
| API Gateway 라우팅 | Path=/auth/** → Auth-Guard |
| Auth-Guard 실제 경로 | /token/refresh (클래스 레벨 @RequestMapping 없음) |
프론트: /auth/token/refresh
↓
API Gateway: /auth/** 매칭 → Auth-Guard로 전달
↓
Auth-Guard: /auth/token/refresh 받음
↓
404! (실제로는 /token/refresh만 존재)
해결 방안 (백엔드 팀 전달)#
옵션 1: StripPrefix 필터 추가 (권장)
routes:
- id: auth-guard
uri: ${AUTH_GUARD_URL}
predicates:
- Path=/auth/**
filters:
- StripPrefix=1 # /auth/token/refresh → /token/refresh
옵션 2: Controller에 @RequestMapping 추가
@RestController
@RequestMapping("/auth") // 추가
public class AuthController {
4. ArgoCD 대시보드 개선#
변경사항#
대시보드 분리
argocd-app-deployments.json- 서비스 앱 (dev-*)argocd-infra-deployments.json- 인프라 (Istio, Monitoring 등)
Unhealthy 쿼리 수정
- 기존:
health_status=~"Degraded|Progressing|Unknown"(Progressing도 Unhealthy로 카운트) - 변경:
health_status="Degraded"+OR on() vector(0)(결과 없을 때 0 표시)
- 기존:
로그 섹션 추가 (두 대시보드 동일)
- Reconciliation 건수 (Loki timeseries)
- 에러 로그 (Loki logs)
- Webhook 수신 로그
- Sync 완료 로그
기본 시간 범위 변경
now-24h→now-30m- Loki 쿼리 타임아웃 문제 해결
헤더 한 줄로 변경 (스크롤바 제거)
파일 변경 목록#
생성#
dev/root/infra/security/swagger-403-redirect.yaml
수정#
common-charts/.../argocd-app-deployments.jsoncommon-charts/.../argocd-infra-deployments.jsondev/root/kustomization.yaml(swagger-403-redirect 등록)
5. Helm Chart Dependency 관리#
문제#
Error: An error occurred while checking for chart dependencies.
You may need to run `helm dependency build` to fetch missing dependencies:
found in Chart.yaml, but missing in charts/ directory: base
ArgoCD가 helm template 실행 시 dependency를 찾지 못함.
원인#
- Helm chart의
Chart.yaml에 dependencies 정의됨 helm dependency build실행 시charts/폴더에 다운로드됨.gitignore에charts/가 포함되어 있거나, 커밋되지 않은 경우 ArgoCD가 사용 불가
해결: 주기적 dependency 업데이트#
# 1. Helm repo 추가 (최초 1회)
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo add calico https://docs.tigera.io/calico/charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
# 2. Dependency 빌드
helm dependency build common-charts/infra/istio/base
helm dependency build common-charts/infra/calico
helm dependency build common-charts/infra/monitoring/loki
# 3. charts/ 폴더 커밋
git add common-charts/**/charts/
git commit -m "chore(helm): update chart dependencies"
git push
왜 주기적으로 해야 하는가?#
- 버전 업그레이드: upstream chart 버전이 올라가면
Chart.yaml수정 후 rebuild 필요 - Chart.lock 불일치: 로컬과 원격 간 lock 파일 불일치 시 rebuild
- 캐시 문제: ArgoCD repo-server 캐시가 오래된 경우
자동화 방안 (TODO)#
- CI/CD에서
helm dependency build자동 실행 - 또는 ArgoCD Application에
helm.skipCrds: true+ pre-sync hook
6. EnvoyFilter dynamic metadata 이슈#
문제#
- EnvoyFilter의
envoy_on_response에서:authority헤더 접근 시 nil 반환
원인#
:authority는 요청 헤더이며, 응답 단계에서 접근 불가- Envoy Lua 필터에서 요청 정보를 응답 단계로 전달하려면 dynamic metadata 사용 필요
해결#
-- 요청 단계: 메타데이터에 저장
function envoy_on_request(request_handle)
local host = request_handle:headers():get(":authority") or ""
local path = request_handle:headers():get(":path") or "/"
if string.find(host, "swagger") then
request_handle:streamInfo():dynamicMetadata():set("swagger_filter", "is_swagger", "true")
request_handle:streamInfo():dynamicMetadata():set("swagger_filter", "original_path", path)
end
end
-- 응답 단계: 메타데이터에서 읽기
function envoy_on_response(response_handle)
local metadata = response_handle:streamInfo():dynamicMetadata():get("swagger_filter")
if metadata and metadata["is_swagger"] == "true" then
-- 처리 로직
end
end
7. Istio IngressGateway 라우팅 싱크 문제#
문제#
- VirtualService/EnvoyFilter 변경 후 라우팅이 즉시 반영 안 됨
istioctl proxy-status에서 SYNCED인데도 404 발생
원인#
- Istiod → IngressGateway 간 config push 지연
- 여러 VirtualService가 같은 host를 참조할 때 충돌 가능성
해결#
# IngressGateway 재시작 (가장 확실)
kubectl rollout restart deployment istio-ingressgateway -n istio-system
# 또는 Istiod 재시작 (모든 프록시에 config 재전송)
kubectl rollout restart deployment istiod -n istio-system
예방#
- 불필요한 VirtualService 정리 (
faro-receiver등) - 삭제된 서비스를 참조하는 VirtualService 제거
커밋#
fix(grafana): standardize ArgoCD dashboard structure and fix Unhealthy query
feat(istio): add swagger 403→login redirect EnvoyFilter
fix(istio): use dynamic metadata for EnvoyFilter cross-phase header access
chore(helm): add chart dependencies for istio, calico, loki