ice rabbit programming

[nginx] nginx에서 auth_request 활용하기(feat. conditional) 본문

Development/Web etc.

[nginx] nginx에서 auth_request 활용하기(feat. conditional)

판교토끼 2021. 2. 19. 02:59

이전 글에서 nginx의 proxy_pass에 대해서 포스팅했었다. 이번 포스팅에서는 reverse proxy할 때, 인증을 거칠 수 있는 auth_request에 관해 포스팅하려 한다.

대개의 웹 서비스는 인증/권한 관리 시스템을 갖추고 있고, 허용된 요청에 한해서만 정상적인 response를 반환한다. 예를 들어 로그인이 되어 있지 않은 사용자는, 로그인이 필요한 페이지에 접근 시에 로그인 페이지로 redirect 하는 경우가 있다. API 레벨이나 프론트엔드에서도 각각 처리가 필요하지만, 기본적으로 nginx에서 일차적으로 걸러주는 것이 필요하다. 이럴 때 auth_request를 사용할 수 있다.

auth_request는 말 그대로 인증이 유효한지를 보내는 것인데, token을 사용한다면 이 token이 유효한지 체크하는 API가 함께 제공될(혹은 만들었을) 것이다. 이를 통해 체크해보고 nginx가 proxy_pass를 할지 판단하는 것이다. 예시는 아래와 같다.

location / {
    auth_request /auth_test;
    proxy_pass http://my_domain/api;
    error_page 500 http://my_domain/login;
}

location /auth_test {
    internal;
    // 각종 설정들
    proxy_set_header accept "application/json";
    proxy_set_header authorization "Basic my_secret";
    proxy_pass http://my_domain/token/check?token=$http_authorization; // token을 check할 url
}

auth_test라는 내부용 location을 생성하고, token을 체크하도록 한다. 설정은 예시로 적용한 것이고, token 체크할 때 필요한 헤더를 적절히 적용해주면 된다. $http_authorization은 헤더에 실어서 온 authorization 값(주로 token이 들어있는 곳이다)을 가져오는 변수인데, 만일 다르게 가져오고 있다면 변환해주면 된다.
(이 때 token 체크는 유효하지 않으면 에러를 반환한다고 생각하자)

/ 에 접근했을 경우 auth_test에 토큰 체크를 보내 유효한지 확인하는데, 만약 2xx의 정상적인 status 응답 코드라면 아래 proxy_pass를 타게 되고, 에러가 반환되면 에러 페이지를 가게 된다. 이 때 주의할 점은, auth_test에서 400(권한 오류) 에러를 뱉어주더라고, auth_reqeust를 보낸 location / 내부에서는 500 에러로 처리된다. 위 코드에서는 에러가 발생했을 경우 로그인 페이지로 보내주고 있다.

이런 식으로, auth를 체크하여 분기 처리할 수 있다.

조건부 auth_request

기획적인 문제나 flow 상의 문제 등으로 인해 auth_request를 조건부로 처리하고 싶은 경우가 종종 있다. 하지만 auth_request 구문은 if문 내에서 동작하지 않는다. 만일 if문 내에 auth_request를 넣으면 <"auth_request" directive is not allowed here in.> 에러가 발생하면서 동작하지 않는다.

본인도 이런 필요성이 있어서 여러 가지로 검색해 보았는데, 공식적인 해결법은 찾지 못하였고, stackoverflow에서 가장 많이 보이는 답변은 rewrite를 통한 우회적 분기 처리였다.

rewrite는 간단히 말해 redirect 시키는 것인데, 즉 auth_request를 직접 하는 것이 아닌 다른 내부 location으로 redirect 시켜서 처리를 떠넘기는 것이다. 아래처럼 쓸 수 있다.

location / {
    // ...
    if (어떤 조건) {
        rewrite .* /_conditional_auth_test last;
    }
    // ...
}

location /_conditional_auth_test {
    internal;
    auth_request /auth_test;
    proxy_pass http://my_domain/api;
    error_page 500 http://my_domain/login;
}

이런 식으로 다른 location으로 redirect하여, auth_request를 보내는 식으로 처리한다. 퍼포먼스가 좋을 것 같지는 않지만, 의도한대로 동작한다. 상술했듯이 오피셜한 해결법이 없고, 올바른 방법인지는 모르겠으나 의도한대로 동작하고 있기 때문에 개인적으로는 이런 rewrite를 통한 방법을 최소한으로 적용한다면 괜찮지 않을까 한다.

참고로 rewrite에 last 옵션을 주면 이후 줄들은 실행되지 않는다.