4. 인증/권한(RBAC) 붙이기: auth_request + 헤더 계약 + 경로별 정책
auth_request와 ‘헤더 계약’을 활용해, 경로별 RBAC(권한 분리)를 붙이는 실무 패턴을 정리해 드립니다.
이 글의 결론
포털/대시보드에서 RBAC를 “앱에서 if문”으로만 처리하면 유지보수가 급격히 어려워진다.
NGINX에서 인증 통과 여부(2xx/401/403) + 사용자/그룹 헤더 계약을 먼저 고정해두면, React/Node는 “신뢰된 사용자 정보”만 받아 훨씬 단순해진다. (auth_request 기반)
1) RBAC를 NGINX에서 하는 이유
- 정책의 단일화: 서비스가 늘어도 “접속 정책”은 NGINX 한 곳에서 통제
- 감사/추적: 접근 차단(403)도 동일한 로깅/알림 체계로 남김
- 코드 단순화: 앱(React/Node)은 “권한 계산”보다 “업무 로직”에 집중
- 장애 격리: IdP/인증이 이상해도 영향 범위를 프록시 레벨에서 제어
2) 구조
→ 2) NGINX가 auth subrequest(/_auth)로 인증 확인
→ 3) 2xx면 통과, 401/403이면 차단
→ 4) 통과 시 사용자/그룹 헤더를 업스트림에 전달
✔️ 핵심 포인트는 “인증 결과는 subrequest의 HTTP 코드(2xx/401/403)로 결정”되고,
✔️ “사용자/그룹/역할은 헤더 계약으로 고정해서 Upstream에 넘긴다”는 점입니다.
3) 핵심 개념: auth_request + auth_request_set
auth_request는 “서브요청(subrequest)”을 던져서 그 결과로 접근을 허용/차단합니다. 서브요청이 2xx면 허용, 401/403이면 차단(해당 코드로 응답)입니다.
그리고 auth_request_set로 “인증 게이트웨이가 준 헤더”를 NGINX 변수로 받아서, 업스트림(React/Node)에 전달할 수 있습니다.
예시: /_auth 서브요청 + 사용자/그룹 헤더를 변수로 저장
# /etc/nginx/conf.d/40-auth-request.conf (예시)
# 1) 인증 체크 서브요청 엔드포인트
location = /_auth {
internal;
proxy_pass http://127.0.0.1:4180/oauth2/auth;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 2) 서브요청 응답 헤더를 변수로 추출(헤더명은 환경에 맞게 조정)
auth_request_set $auth_user $upstream_http_x_auth_request_user;
auth_request_set $auth_email $upstream_http_x_auth_request_email;
auth_request_set $auth_groups $upstream_http_x_auth_request_groups;
⚠️ 운영자 팁(중요)
“RBAC가 안 먹는다”의 80%는 그룹/역할이 어떤 헤더로 오고 있는지 계약이 없어서 생긴다.
일단 로그로 $auth_groups를 찍어 실제 값이 뭔지부터 확인하자.
4) “그룹 → 역할” 매핑(map)으로 경로별 RBAC
이제 $auth_groups(문자열)에 들어있는 그룹을 기준으로 역할을 만들고, /admin, /ops 같은 경로에 정책을 강제합니다.
(A) 그룹 문자열을 역할로 변환(map)
# /etc/nginx/conf.d/50-rbac-map.conf
# auth_groups 예: "admins,operators,readonly" 처럼 들어온다고 가정
# (정규식은 환경에 맞게 튜닝)
map $auth_groups $role {
default "readonly";
~*admins "admin";
~*operators "operator";
}
(B) 경로별 정책 강제(403/401 분리)
# server{} 또는 location{} 예시
# 공통 보호 구간
location / {
auth_request /_auth;
error_page 401 = /oauth2/sign_in;
# 업스트림으로 사용자 정보 전달(앱은 이 값만 믿으면 됨)
proxy_set_header X-Auth-User $auth_user;
proxy_set_header X-Auth-Email $auth_email;
proxy_set_header X-Auth-Groups $auth_groups;
proxy_set_header X-Auth-Role $role;
proxy_pass http://127.0.0.1:3000;
}
# 관리자 전용 경로
location ^~ /admin/ {
auth_request /_auth;
error_page 401 = /oauth2/sign_in;
# role이 admin 아니면 403
if ($role != "admin") { return 403; }
proxy_set_header X-Auth-Role $role;
proxy_pass http://127.0.0.1:3000;
}
# 운영자/관리자 경로
location ^~ /ops/ {
auth_request /_auth;
error_page 401 = /oauth2/sign_in;
if ($role !~ ^(admin|operator)$) { return 403; }
proxy_set_header X-Auth-Role $role;
proxy_pass http://127.0.0.1:3000;
}
왜 401과 403을 분리하나?
401: 로그인/세션 문제(“인증 실패”) → sign_in으로 보내기
403: 로그인은 했는데 권한이 없음(“인가 실패”) → “권한 없음” 안내가 맞음
이 구분이 있어야 운영자/사용자 모두 덜 헷갈린다.
5) 디버깅/검증 커맨드(운영자 필수)
# 1) NGINX 문법 검사
sudo nginx -t
# 2) 헤더/리다이렉트 흐름 확인
curl -kI https://portal.example.com/ | egrep -i 'HTTP/|Location|Set-Cookie'
# 3) 권한 없는 경로(403) 확인
curl -kI https://portal.example.com/admin/ | egrep -i 'HTTP/|Location'
# 4) auth subrequest만 단독으로 확인하고 싶으면(직접 접근은 internal이라 불가)
# → 인증 게이트웨이(/oauth2/auth)를 직접 호출해 202/401 등을 확인
curl -kI https://portal.example.com/oauth2/auth
6) 자주 터지는 포인트(실전 Troubleshooting)
- 그룹 문자열 형태가 예상과 다름(쉼표/공백/대문자) → map 정규식/파싱 튜닝
- Host/Proto 헤더가 꼬이면 로그인 리다이렉트가 깨짐 → X-Forwarded-* 확인
- 403인데 화면이 로그인 페이지로 가면, 401/403 처리(error_page)가 섞인 것
- 권한 정책을 앱과 NGINX 둘 다에서 하면 “이중 정책”으로 디버깅이 어려움 → 한 곳을 소스로 정하기
'Tech Note > 서버-Nginx' 카테고리의 다른 글
| 6. 보안 베이스라인: 헤더/접근제어/타임아웃/업로드 제한 “최소 세트” (0) | 2025.12.28 |
|---|---|
| 5. 운영 로그로 APM 만들기: “느림”을 3분해해서 원인 좁히기 (0) | 2025.12.28 |
| 3. 구현 사례 예시 2개(포털 / 정책자동화) (0) | 2025.12.28 |
| 2. 옵션별 설정 “실무 세트”(proxy_pass/헤더/realip/auth_request/로그/WS/RateLimit) (0) | 2025.12.28 |
| 1. 설치/폴더 구성/운영 명령어 (0) | 2025.12.28 |