Published on

Kong API Gateway MSA 라우팅·JWT 401 트러블슈팅

Authors

MSA 환경에서 Kong API Gateway를 붙이면, 라우팅과 인증이 한곳에 모이면서 운영 난이도가 크게 내려갑니다. 반대로 말하면, 라우팅이 한번 어긋나거나 JWT가 한번 401로 떨어지기 시작하면 장애 영향 범위도 한 번에 커집니다.

이 글은 다음 두 가지를 한 번에 다룹니다.

  • Kong에서 MSA 라우팅이 예상과 다르게 매칭되는 문제(잘못된 서비스로 프록시, 404, 프리플라이트 실패 등)
  • Kong JWT 플러그인(또는 OIDC 연동)에서 401이 발생할 때의 원인 분해와 재현 가능한 해결 절차

운영 관점에서 중요한 것은 “원인을 빨리 좁히는 순서”입니다. 아래 체크리스트는 실제 현장에서 시간을 가장 많이 잡아먹는 지점을 우선순위로 정리했습니다.

문제를 빨리 좁히는 1차 분류

증상을 다음 3개로 먼저 분류하면 디버깅 시간이 확 줄어듭니다.

1) 라우팅 자체가 틀림

  • /api/users를 쳤는데 주문 서비스로 감
  • 특정 경로만 404 또는 405
  • 동일한 경로가 환경(스테이징/프로덕션)마다 다르게 동작

대부분 Route의 매칭 규칙(우선순위, strip path, path handling, hosts, methods) 문제입니다.

2) 라우팅은 맞는데 업스트림에서 401/403

  • Kong 응답이 아니라 업스트림 서비스가 401을 반환
  • X-Consumer-* 헤더가 없거나, Authorization 전달이 누락

이 경우는 “Kong 인증 플러그인이 막는지” vs “업스트림이 막는지”를 분리해야 합니다.

3) Kong에서 직접 401을 반환

  • 응답 바디에 Unauthorized 또는 JWT 관련 메시지
  • Kong 로그에 jwt 플러그인 에러가 남음

대부분 토큰 서명 검증 실패, 키/시크릿 불일치, 클레임 불일치, 헤더/쿼리에서 토큰 추출 실패입니다.

라우팅 트러블슈팅: Route 매칭 규칙부터 고정하기

Kong 라우팅 문제의 핵심은 “내 요청이 어떤 Route에 매칭됐는지”를 확인하는 것입니다.

1) Route 우선순위와 매칭 조건 점검

Kong은 여러 Route가 동시에 매칭될 수 있으면 우선순위 규칙에 따라 하나를 선택합니다. 흔한 사고 패턴은 다음과 같습니다.

  • 너무 포괄적인 경로(예: /api) Route가 먼저 잡아먹음
  • hosts 기반 라우팅과 paths 기반 라우팅이 섞여 의도치 않은 매칭 발생
  • strip_path 설정 때문에 업스트림이 기대하는 경로가 달라짐

관리 API 또는 deck로 Route를 확인할 때는 다음 필드를 우선 봅니다.

  • paths, hosts, methods
  • strip_path
  • path_handling

예시 deck 설정(간단화):

_format_version: "3.0"
services:
  - name: user-svc
    url: http://user:8080
    routes:
      - name: user-route
        paths:
          - /api/users
        strip_path: false
        path_handling: v1

  - name: api-catchall
    url: http://gateway-fallback:8080
    routes:
      - name: api-route
        paths:
          - /api
        strip_path: false
        path_handling: v1

위 구성에서 /api/users는 두 Route에 모두 매칭될 수 있습니다. 이때 “더 구체적인 경로가 이긴다”는 직관이 항상 맞지 않는 버전/설정 조합이 있고, 운영 중 변경으로 우선순위가 흔들릴 수 있습니다. 따라서 포괄 Route는 가급적 분리하거나, hosts/methods로 매칭을 더 좁혀 충돌 가능성을 줄이는 쪽이 안전합니다.

2) strip_path 때문에 업스트림이 404를 내는 경우

예를 들어 업스트림이 /api/users를 그대로 기대하는데, Route에서 strip_path: true라면 업스트림에는 / 또는 /users로 전달될 수 있습니다.

재현 테스트:

curl -i http://kong:8000/api/users
  • Kong이 404를 주면 라우팅 매칭 실패 가능성이 큼
  • 업스트림이 404를 주면 경로 전달이 달라졌을 가능성이 큼

Kong 액세스 로그에 업스트림 URI가 어떻게 전달되는지 확인하세요.

3) 프리플라이트(OPTIONS)만 실패하는 케이스

브라우저에서만 실패하고 서버 간 호출은 성공한다면, 거의 항상 CORS 프리플라이트가 Route 매칭에 걸립니다.

  • Route에 methods가 걸려 있는데 OPTIONS가 빠짐
  • CORS 플러그인이 특정 Route에만 붙어 있음

해결 방향:

  • 프리플라이트를 처리할 Route/Service에 CORS 플러그인을 일관되게 적용
  • methods를 제한한다면 OPTIONS 포함

JWT 401 트러블슈팅: “토큰 추출”부터 “서명 검증”까지 단계별 점검

JWT 401은 원인이 다양하지만, 디버깅 순서는 거의 고정입니다.

  1. Kong이 토큰을 제대로 추출했는가
  2. 토큰 형식이 JWT인가
  3. 서명 검증 키가 맞는가
  4. 클레임 검증(iss, aud, exp 등)이 맞는가
  5. 라우트/서비스에 플러그인이 의도대로 적용됐는가

1) 토큰 추출 실패: Authorization 헤더 포맷

가장 흔한 실수는 Authorization 헤더 포맷이 Kong 설정과 불일치하는 것입니다.

  • Authorization: Bearer ...로 보냈는데 Kong JWT 플러그인이 bearer를 기대하지 않는 설정
  • 헤더 이름을 커스텀으로 썼는데 플러그인이 못 읽음
  • 프록시/로드밸런서에서 Authorization 헤더를 제거

테스트:

curl -i \
  -H "Authorization: Bearer eyJ..." \
  http://kong:8000/api/users/me

Kong JWT 플러그인은 보통 헤더, 쿼리스트링, 쿠키 등에서 토큰을 찾을 수 있는데, 운영에서는 “한 가지 방식으로만 강제”하는 편이 사고가 적습니다.

2) 토큰은 JWT인데도 401: 서명 키 불일치

Kong JWT 플러그인은 “토큰의 서명을 검증할 키/시크릿”이 Kong에 등록돼 있어야 합니다. 여기서 자주 터지는 포인트:

  • 발급자는 RS256(공개키/개인키)인데 Kong에는 HS256(shared secret)로 등록
  • Keycloak 같은 IdP가 키를 회전했는데 Kong에 반영이 안 됨
  • kid(Key ID)로 키 선택이 필요한데 Kong 설정이 단일 키만 바라봄

Kong JWT 플러그인(대칭키 HS256) 기본 예시:

# Consumer 생성
curl -sS -X POST http://kong-admin:8001/consumers \
  --data "username=mobile-app"

# JWT credential 생성 (secret 기반)
curl -sS -X POST http://kong-admin:8001/consumers/mobile-app/jwt \
  --data "key=mobile-app" \
  --data "secret=super-secret" \
  --data "algorithm=HS256"

# 라우트에 jwt 플러그인 적용
curl -sS -X POST http://kong-admin:8001/routes/user-route/plugins \
  --data "name=jwt"

운영에서 RS256을 쓰는 경우가 많습니다. 이때는 “JWT 플러그인만으로 해결할지”를 먼저 결정해야 합니다. IdP의 JWKS를 따라가며 키 회전을 자동으로 반영하려면 OpenID Connect 플러그인(또는 외부 인증 서비스)을 고려하는 편이 안전합니다.

Keycloak을 쓰는 경우, 리다이렉트/issuer 불일치로 인증이 무너지는 패턴이 자주 나오니 원인 분해는 아래 글도 함께 참고하면 좋습니다.

3) iss aud exp 검증 실패

Kong이 401을 내는데 서명 키는 맞는 것 같다면, 다음을 확인합니다.

  • exp가 이미 지났거나, 서버 시간(NTP)이 밀림
  • iss가 환경별로 다름(스테이징과 프로덕션 issuer 혼동)
  • aud가 프론트/백엔드/모바일마다 다르고 게이트웨이가 하나만 허용

특히 클라우드 환경에서 시간 오차는 생각보다 흔합니다. 컨테이너 노드 시간 동기화가 깨지면 JWT는 전부 401이 됩니다.

4) 라우트에 플러그인이 “생각한 곳”에 붙어 있지 않음

Kong은 플러그인을 Service, Route, Consumer 등 여러 레벨에 붙일 수 있습니다. 운영 중 설정이 누적되면 다음 문제가 생깁니다.

  • 어떤 Route는 JWT가 적용되고 어떤 Route는 빠져서 보안 구멍 발생
  • 글로벌로 JWT를 걸었는데 헬스체크/메트릭까지 막혀 장애 확대
  • 특정 Consumer만 예외 처리하려다 예상치 못한 조합이 생김

권장 방식은 “인증이 필요한 API 그룹”을 Route 단위로 명확히 묶고, 예외(헬스체크, 로그인, 토큰 갱신)는 별도 Route로 분리하는 것입니다.

Kong 로그/메트릭으로 401 원인을 빠르게 찾는 법

1) Kong이 401을 만들었는지, 업스트림이 401을 만들었는지

  • Kong이 만든 401은 보통 응답 헤더/바디가 단순하고, 로그에 플러그인 에러가 남습니다.
  • 업스트림이 만든 401은 업스트림의 응답 포맷(JSON 에러 바디 등)이 그대로 내려오는 경우가 많습니다.

관측 포인트:

  • status가 401인 요청에서 upstream_status가 존재하는지
  • latency가 매우 짧으면 Kong에서 컷했을 가능성이 큼

2) 상관관계 ID로 end-to-end 추적

MSA에서는 401 자체보다 “왜 이 요청이 이 Route로 갔는지”가 핵심이 되는 경우가 많습니다. Kong에서 요청 ID를 강제하고 업스트림으로 전달하세요.

예시(Nginx/Lua 기반 커스텀을 쓰지 않는 선에서 최소화):

  • Kong 기본 request id 헤더를 로깅
  • 업스트림 서비스 로그에 동일 헤더를 남김

타임아웃/데드라인 전파가 누락되면 인증 서버/유저 서비스가 병목일 때 401처럼 보이는 5xx/timeout이 섞여 들어오기도 합니다. 관련해서는 아래 글의 “전파” 관점이 도움이 됩니다.

실전 구성 예시: 경로 기반 라우팅 + JWT 보호 + 예외 라우트

아래는 운영에서 자주 쓰는 “보호 구간과 예외 구간을 라우트로 분리”하는 패턴입니다.

  • /api/auth/*는 인증 예외(로그인/리프레시)
  • /api/*는 JWT 필수
_format_version: "3.0"
services:
  - name: auth-svc
    url: http://auth:8080
    routes:
      - name: auth-public
        paths:
          - /api/auth
        strip_path: false
        path_handling: v1

  - name: api-svc
    url: http://bff:8080
    routes:
      - name: api-protected
        paths:
          - /api
        strip_path: false
        path_handling: v1
        plugins:
          - name: jwt
            config:
              key_claim_name: iss
              claims_to_verify:
                - exp

포인트:

  • 예외를 “플러그인 disable”로 처리하지 말고, 라우트 자체를 분리해 정책을 단순화합니다.
  • key_claim_name 같은 옵션은 팀 내 토큰 규약에 맞춰 명확히 합의해야 합니다.

자주 발생하는 원인별 체크리스트(현장용)

라우팅

  • 포괄 경로 Route(/api)가 구체 경로(/api/users)를 먹지 않는지
  • strip_path로 업스트림 경로가 변형되지 않는지
  • methods 제한에 OPTIONS가 빠지지 않았는지
  • hosts 기반과 paths 기반을 섞어 “환경별 host 차이”가 라우팅을 바꾸지 않는지

JWT 401

  • Authorization 헤더가 실제로 Kong까지 도달하는지(중간 프록시에서 제거되는지)
  • 토큰이 진짜 JWT인지(세션 토큰/opaque token을 JWT로 착각하지 않는지)
  • 알고리즘(HS256 vs RS256) 불일치가 없는지
  • 키 회전(kid/JWKS) 시나리오를 고려했는지
  • 서버 시간 오차로 exp 검증이 깨지지 않는지
  • 플러그인이 Service/Route 어디에 붙었는지, 예외 라우트가 분리됐는지

마무리: “정책 단순화”가 401을 줄인다

Kong에서 라우팅과 JWT 401을 안정화하는 가장 좋은 방법은 복잡한 예외 처리를 늘리는 게 아니라, 라우트 설계를 단순하게 만들고(보호/비보호 분리), 토큰 규약을 하나로 고정하고(헤더 포맷, 알고리즘, issuer/audience), 키 회전 전략(JWKS 자동 반영 여부)을 운영 표준으로 박아두는 것입니다.

401이 터졌을 때는 감으로 설정을 바꾸기보다, 이 글의 순서대로 “토큰 추출”부터 “서명/클레임 검증”, 그리고 “플러그인 적용 범위”를 단계적으로 확인하면 대부분 재현 가능하게 원인을 찾을 수 있습니다.