SW개발/개발이야기

[Django]Openstack Swift 401 Authentication failed 삽질기

Django에서 Object storage 기술을 사용하기 위해 서드파티 라이브러리를 활용하여 개발한 도중 겪었던 401 Authentication 에러에 대해 포스팅 해보려고 합니다.

 

제가 사용했던 오픈소스는 다음과 같습니다. (python-swiftclient는 django-storage-swift에 의존하는 라이브러리)

https://github.com/dennisv/django-storage-swift

 

GitHub - dennisv/django-storage-swift: OpenStack Swift storage backend for Django

OpenStack Swift storage backend for Django. Contribute to dennisv/django-storage-swift development by creating an account on GitHub.

github.com

https://github.com/openstack/python-swiftclient

 

GitHub - openstack/python-swiftclient: OpenStack Storage (Swift) Client. Mirror of code maintained at opendev.org.

OpenStack Storage (Swift) Client. Mirror of code maintained at opendev.org. - GitHub - openstack/python-swiftclient: OpenStack Storage (Swift) Client. Mirror of code maintained at opendev.org.

github.com

 

401 Authentication 에러와의 만남

django-storage-swift 라이브러리는 settings.py에 설정값을 지정해주는 것만으로 간단하게 사용할 수 있습니다. 그렇기 때문에 큰 수고를 들이지 않고도 object storage 연결을 할 수 있습니다. (편리한 세팅이 가장 큰 장점입니다)

 

하지만, 이 라이브러리를 활용하면서 큰 문제점이 한 가지 존재하였습니다. 어느 이유에서인지는 전혀 모른체 항상 특정 시간만 되면 401 Authentication failed 에러와 함께 업로드가 5~10분 정도 되지 않는 현상이 발생하였습니다. 하지만, 조금 시간이 지나고나면 정상적으로 작동하였습니다.

 

더군다나, Sentry의 에러 로그를 보더라도 401 에러 이외에는 별다른 정보를 찾기가 매우 힘들어 어느 상황에서 오류가 발생하는지 찾는 데 꽤 오랜 시간이 걸렸던 것 같습니다.

 

에러의 원인을 발견

그렇게 오랜 시간이 지난 후에, 한 팀원이 토큰 갱신시간과 연관이 있다는 것을 알아내게 되었습니다. 사용하는 object storage cloud의 토큰 갱신시간이 24시간인데, 정확히 24시간의 사이클과 맞물리는 시점에서 항상 에러가 발생한다는 것이었습니다. 그렇기에 토큰의 갱신시간을 새벽시간대로 조정하여 임시방편으로 에러를 해결할 수 있었습니다.

 

하지만 여전히 임시방편일 뿐, 똑같이 새벽에 토큰이 갱신되는 시점에 같은 에러가 발생할 가능성은 내포하고 있습니다. 어떠한 로직으로 인증이 이루어지는지 알아보기 위해 오픈 소스를 뜯어보기 시작합니다.

 

오픈소스를 본격적으로 뜯어보다..!

storage.py에서 토큰에 대한 getter, setter 함수를 찾을 수 있었습니다. 해당부분을 중점적으로 살펴보았습니다.

def get_token(self):
    if time() - self._token_creation_time >= self.auth_token_duration:
        new_token = swiftclient.get_auth(
            self.api_auth_url,
            self.api_username,
            self.api_key,
            auth_version=self.auth_version,
            os_options=self.os_options)[1]
        self.token = new_token
    return self._token

def set_token(self, new_token):
    self._token_creation_time = time()
    self._token = new_token
    
token = property(get_token, set_token)

auth_token_duration은 토큰이 만료되기로 기대되어지는 시간에 대한 값입니다. default는 23시간이며, 저는 10분의 값을 설정해두었습니다.

 

위의 로직에 대해서 간략하게 설명해보겠습니다.

  1. 현재 시간 - 토큰 생성시간 >= 10분이라는 조건에 부합하면 cloud 서버에서 새로운 토큰을 가져옴
  2. 가져온 토큰을 새롭게 할당해주면서 setter 함수 호출, 이 과정에서 _token_creation_time 현재로 갱신

해당로직을 풀이하던 도중, 토큰의 갱신시간과 관련하여 이상하다는 느낌을 받게 되었습니다.

첫째로, 토큰이 갱신되지 않았음에도 불구하고 새롭게 set 해준다는 것.

둘째로, 토큰이 새롭게 set 될 때마다 _token_creation_time이 변한다는 것.

 

두개가 맞물려서 해당 로직에 대한 허점을 만들어내고 있었습니다. 토큰의 갱신 주기마다 에러가 발생하는 점도 이곳의 문제임을 확신할 수 있었습니다.

 

예를 들어, 토큰의 갱신되는 시간이 매일 오후 6시라고 가정해보겠습니다. 

만약 5시 55분에 한 사용자가 업로드를 했다면, 이 때 토큰의 생성시간은 5시 55분으로 갱신이 됩니다. 

그리고 6시가 넘어 이전의 토큰이 사라지고 새로운 토큰으로 대체가 되었습니다. 이 사용자가 6시 2분경 다시 파일 업로드를 시도합니다.

이 때, if문의 로직에 의하여 7분 > 10분 으로 False를 반환하게 되어 새롭게 바뀐 토큰을 사용하지 못하고 이전의 토큰을 사용하게 됩니다.

이 과정에서 401 Authentication failed 에러가 발생하게 됩니다. (이전의 토큰으로 인증을 시도하였기 때문)

 

에러를 해결하다

auth_token_duration의 값을 1분정도로 내린다면 에러가 발생하는 확률을 낮출 수 있었습니다. 하지만, 이렇게 한다면 불필요한 요청만 늘어날 뿐, 근본적인 해결책은 아니라고 생각하였습니다.

 

따라서 저는 해당 함수를 오버라이드 하여 로직을 다시 작성하기로 결정하였습니다. 우선적으로 토큰이 바뀌지 않는다면 _token_creation_time 역시 바뀔 필요가 없도록 해두었고 auth_token_duration 시간 대신 토큰의 expire 시간을 가져와서 처리하는 방법을 택하였습니다.

 

처리를 완료하고 테스트를 해보면서 혹시 github issue와 PR에 저와 같은 경우는 없는 지 하나씩 찾아보기 시작합니다. 그러던 중, 아래와 같은 PR을 발견하게 됩니다.

https://github.com/dennisv/django-storage-swift/pull/11/commits/36e5e320381d8f22955e8e38145955ea83b9db40

 

X-Auth-Token is invalid and should be refreshed after 24 hours. by stefan8 · Pull Request #11 · dennisv/django-storage-swift

Token is created when storage is instatiated and swift invalidate token within 24 hours, so we get 401 error if server is not reloaded.

github.com

 

제가 사용하는 패키지 버전은 1.12.17 이었으며, 최신버전에는 해당 이슈가 수정되어 있었습니다. 제가 구성했던 로직과는 상이하였지만 근본적으로 라이브러리에서 해결한 버전을 사용하는 것이 맞다는 판단하에 버전을 업그레이드 하는 것으로 문제를 해결할 수 있었습니다.

 

마치며..

개발을 진행하다보면 에러는 수없이 발생하기 마련입니다. 쉽게 처리할 수 있는 에러라면 다행이지만 가끔은 어떤 에러가 오랫동안 속을 썩이기도 합니다. 만약, 문제가 될 만한 요소들을 전부 체크했는데도 계속 해결할 수 없다면 사용하고 있는 버전, 이슈등을 체크해 보는 편이 좋습니다. 실제로 꽤 많은 문제들이 버전의 차이로 인해서 생기게 됩니다.

 

오픈소스를 뜯어 보면서 기여해보고 싶다는 목표가 생겼습니다. 시간이 된다면 오픈소스를 정해서 버그 수정과 같은 이슈를 해결해보려고 합니다. 읽어주셔서 감사합니다.

 

 

728x90