TL;DR
- 웹사이트를 수정하지 않고 Nginx 프록시 사이트를 보호하려면 HTTP 기본 인증(HTTP Basic Auth)으로 로그인한 뒤 로그인 쿠키를 설정하는 방식을 적용함.
ngx_http_auth_basic_module만 사용하면 일부 브라우저에서 페이지를 열 때마다 인증을 다시 요구하므로, 유효한 쿠키가 있으면 접근을 허용하고 없으면 로그인 경로로 리디렉션함.login_cookies파일은 사용자 이름과 무작위 문자열을 결합한 쿠키 값을 보관하며,.htpasswd파일은htpasswd명령으로 생성함.- Nginx의
map지시어로 쿠키의 사용자 이름과 예상 쿠키 값을 대조하고, 로그인 성공 후 원래 요청한 경로로 리디렉션함. - 내부 웹 서버에 SSH 전달 유닉스 소켓으로 연결하는 경우에도 각 보호 대상 위치에서 인증을 검사하고,
X-Authenticated-User헤더로 사용자 이름을 전달함.
문제
- 웹사이트 자체를 수정하지 않고 Nginx로 프록시하는 사이트에 비밀번호를 설정하려는 상황임.
- 개인용 단순 사이트이므로 일반 로그인 양식 대신 HTTP 인증을 사용할 때의 UI 문제는 감수할 수 있었음.
- Nginx의
ngx_http_auth_basic_module로 HTTP 인증을 설정하는 과정은 간단했지만, 일부 브라우저에서는 페이지를 열 때마다 사용자 이름과 비밀번호를 다시 입력하라는 창이 나타남. - 비밀번호 관리자를 이용하면 추가 클릭만 하면 되지만, 여전히 좋지 않은 사용자 경험임.
해결 방법
- 일반적인 로그인 과정처럼 로그인 쿠키를 설정해 반복 인증 요청을 피하는 여러 사례를 참고했으며, Nginx 설정에 적용하는 과정은 까다로웠고 기존 예제가 그대로 동작하지 않아 이를 바탕으로 다른 구성을 작성함.
- 예제는
example.com서버에서https://example.com/private/하위 경로를 비밀번호로 보호함. login_cookies파일을 사용자 이름별 쿠키 값의 대응표로 불러옴. 각 값은 사용자 이름,|문자, 무작위 문자열을 이어 붙인 형태임.- 쿠키에서 사용자 이름을 추출하고, 클라이언트가 보낸 쿠키와 해당 사용자의 예상 쿠키 값이 일치하는지 검사함.
- 인증되지 않은 요청은
/private/login?redirect=...로 리디렉션됨. 로그인 경로에는auth_basic과.htpasswd를 설정하고, 인증이 성공하면@login_success에서 쿠키를 설정한 뒤 원래 요청 경로로 돌려보냄. .htpasswd파일은htpasswd명령으로 만듦. 파일을 처음 생성할 때는-c옵션이 필요하며, 예제에서는alice와bob계정을 추가함.generate_cookies.sh스크립트는.htpasswd에서 사용자 이름을 읽고, 사용자별로openssl rand -base64 33을 사용해 무작위 문자열을 생성해login_cookies내용을 만듦.login_cookies와.htpasswd에는 비밀 정보가 있으므로 웹 서버 사용자만 읽을 수 있게 설정하고, 웹 서버가 제공하는 경로 밖에 저장해야 함.
작동 방식
- 이 구성은 Nginx
map지시어의 평가 시점에 의존함.map은 처음 사용될 때까지 평가되지 않으므로, 변수가 참조되는 시점을 조절해 계산에 쓰이는 입력을 제어함. - 로그인 쿠키가 있는 페이지 요청에서는 쿠키를 검증하고 요청을 허용함. 유효한 쿠키가 없으면 HTTP 기본 인증이 설정된 로그인 경로로 보내 브라우저의 인증 창을 띄움.
- 로그인이 성공하면 쿠키를 설정하고 사용자를 원래 요청한 페이지로 돌려보냄.
- 보호 경로에서 인증 여부를 확인할 때 쿠키에서 사용자 이름을 추출하고, 그 이름에 해당하는 예상 쿠키 값을
login_cookies에서 찾아 클라이언트 쿠키와 비교함. 두 값이 일치하면 인증 결과가1이 되어 접근을 허용함. - 로그인 경로에서는 Nginx의
$remote_user값으로 사용자 이름을 지정함. 이 값은 HTTP 인증 헤더의 사용자 이름이며, 이를 바탕으로 해당 사용자의 쿠키를 선택해 설정함. $redirect_target은 요청 URL에서redirect매개변수의 경로를 추출함. 정규식(?<redirect>/[^/].*)은 경로가/로 시작하되//로 시작하지 않도록 해 외부 사이트로 향하는 오픈 리디렉션을 막음.- 같은 도메인 안의 다른 경로로 리디렉션하는 것도 문제가 될 수 있다면, 유효한 URL만 허용하도록 정규식을 더 엄격하게 설정할 수 있음.
프록시 웹사이트
- 앞선 예제는 정적 웹사이트를 대상으로 하지만, 실제 사용 사례는 프록시 웹사이트임. 내부 웹 서버에만 접근할 수 있던 사이트를 공개 인터넷에서 비밀번호 보호 상태로 이용하려는 구성임.
- 내부 웹 서버를 가리키는 SSH 전달 유닉스 소켓을 계속 열어 두며, 예시 소켓 경로는
/path/to/proxied-http.sock임. https://example.com/private/localapp/foo요청은 SSH 프록시가 연결되는localcomputer의http://localcomputer/targetapp/foo로 프록시됨./private/localapp위치에서도 사용자 인증 여부를 검사하고, 경로 끝의 슬래시를 정규화한 다음/private/localapp/을/targetapp/으로 다시 작성함.- 프록시 요청에는
X-Real-IP,X-Forwarded-For,X-Forwarded-Proto헤더와 사용자 이름을 담은 비표준X-Authenticated-User헤더를 전달함. 프록시 버퍼링은 끄고 유닉스 소켓을 통해 요청을 전달함. - 인증 검사 조건은 각 위치 블록에 반복해서 넣어야 함.
- 프록시 웹 서버에 직접 연결할 수 있는 클라이언트는
X-Authenticated-User헤더에 임의의 값을 넣을 수 있음. 다만 해당 서버가 이 프록시를 통해서만 비밀번호 보호 상태로 제공되고 직접 접근이 제한돼 있다면 큰 문제가 아닐 수 있음.
로그인 쿠키 확인
- HTTP 기본 인증을 사용하면 브라우저는 보통 사용자 이름과 비밀번호를 한 번 요청한 뒤 다시 묻지 않으며, 비밀번호가 거부된 경우에도 다시 묻지 않는 브라우저가 있음. 로그아웃 기능을 제공하지 않는 브라우저도 있어 로그인 쿠키만으로 인증됐는지 확인하기 어려움.
- 쿠키만으로도 인증이 되는지 확인하기 위해, 비밀번호 확인 없이 고정 사용자
alice의 로그인 쿠키를 설정하는/private/magiclogin위치를 추가함. - 새 브라우저 세션에서
/private/magiclogin에 접속한 다음/private로 이동했을 때 비밀번호 입력을 요구하지 않으면 쿠키 인증이 동작하는 것으로 확인할 수 있음.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요