Spring Boot로 OAuth2 로그인을 구현하고, Caddy를 리버스 프록시로 사용하는 환경에서 다음과 같은 에러가 발생했습니다.
400 오류: redirect_uri_mismatch
Google OAuth Console에는 https://flashnote.rejs.link/login/oauth2/code/google로 등록했는데, 실제로는 http://flashnote.rejs.link/login/oauth2/code/google로 요청이 전송되고 있다.
구글은 authError를 base64로 인코딩해서 보내줍니다. 디코딩하면 자세한 정보를 얻을 수 있다
redirect_uri: http://flashnote.rejs.link/login/oauth2/code/google
http
이것이 오늘의 문제이다.
이 서버는 Caddy를 사용한다. 아래와 같은 장점이 있기 때문이다.
Caddyfile 설정:
flashnote.rejs.link {
reverse_proxy note-app:8080
}
하지만 이 간단한 구조에서 문제가 발생했다.
🏗️ 아키텍처 구조
브라우저 --[HTTPS]--> Caddy --[HTTP]--> Spring Boot (Docker)
(리버스 프록시)
리버스 프록시와 스프링부트사이에는 http 통신을 사용한다. docker를 사용하니까 docker 내부 네트워크 안에서
docker run -it --rm --net container:${해당하는 컨테이너 이름사용할것} nicolaka/netshoot
tcpdump -i eth0 -A port 8080
실제로 caddy가 스프링으로 보내준 패킷과 헤더를 살펴보자.
15:00:07.324901 IP caddy.app-net.40526 > 6c2ac2dc6398.8080: Flags [P.], seq 2441:3291, ack 28725, win 501, options [nop,nop,TS val 3700368177 ecr 3342705723], length 850: HTTP: GET / HTTP/1.1
E.....@....p.........N....i.........[......
..#1.=.;GET / HTTP/1.1
Host: flashnote.rejs.link
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: JSESSIONID=5601152DAE07C6949E02A08E0DB2E264
Priority: u=0, i
Sec-Ch-Ua: "Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
Via: 2.0 Caddy
X-Forwarded-For: /*익명처리*/
X-Forwarded-Host: flashnote.rejs.link
X-Forwarded-Proto: https
그러나 비표준 헤더인 X-Forwarded-Proto에 대한 정보를 처리하지 못해서 자신이 http 서버라고 착각하는 문제가 발생한 것이다.
리버스 프록시가 보내주는 원본요청에 대한 정보 X-Forwarded-*를 스프링에서 사용하는 방법이 없을까?
https://docs.spring.io/spring-boot/how-to/webserver.html#howto.webserver.use-behind-a-proxy-server
server: forward-headers-strategy: framework
스프링은 이미 해답을 가지고 있다.
framework 옵션은 ForwardedHeaderFilter 필터를 사용하여 리버스 프록시가 보내준 정보를 처리한다.
tomcat, netty 등의 컨테이너와 독립적으로 작동한다는 장점이 있다.