ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 중첩된 NAT뒤의 OpenWRT 웹 서버에 터널링하기
    문제해결 2021. 5. 13. 17:41

    목표. NAT뒤의 OpenWRT 웹 서버에 접속하기

    지난 3월부터 끌어온 이슈를 5월 중순이 되어 해결했다. 원했던 기능은 NAT(공유기) 뒤에 호스트에서 실행되는 서버에 접속하는 것이다. 호스트는 우리가 OpenWRT를 기반으로 펌웨어를 다시 올린 공유기이며, OpenWRT에서 동작하는 웹서버인 Luci에 접속하는 것이 목표이다.

     

    클라이언트가 NAT을 경유하여 OpenWRT의 웹서버 Luci에 접속하는 것이 목표다.

     

    방법 1. 포트포워딩

    이를 달성하는 가장 간단한 방법으로는 포트 포워딩이 있다. 공유기에서 제공하는 기능인 포트포워딩은 외부에서 접속하는 포트와 내부 포트를 연결해주는 기능이다. 그러나, 중첩된 NAT(공유기에 공유기를, 다시 또 공유기를 물리는 중첩된 상태)에서 포트 포워딩을 해줘야 하는 경우를 고려해야 했다. 또한, 설치하는 사람이 이러한 설정 방법을 알고 적용해야 하는 것도 문제였다. 매장 마다 쓰는 공유기가 달랐기에 포트 포워딩을 위한 설정 방법 정리하기 어려웠다.

    방법 2. 홀펀칭

    따라서, 포트포워딩이 아닌 다른 방법이 필요했다. 그 방법으로는 홀펀칭이 있다. 홀 펀칭은 NAT뒤에 있는 서버가 외부와 연결할 때 잠시 생기는 포트를 내가 빌려서 마치 내 것처럼 사용하는 기술이다. 포트포워딩을 설정하지 않아도 가능하다.

     

    그러나, UDP홀 펀칭은 HTTP를 사용하므로 탈락했다. TCP홀 펀칭은 구현 난이도가 높고, 공유기 차원에서 지원해주지 않는 경우가 너무 많아 탈락했다. 홀 펀칭 자체가 항상 실패의 가능성이 높아 중개서버로 보완이 필요했다.

    방법 3. 중개 서버, 터널링

    그래서, 직접적으로 두 호스트 간에 연결하는 방법이 아닌, 중개 서버 방식을 고려했다. 중개 서버 방식은 호스트의 서버와 중개 서버를 연결하고, 클라이언트는 중개 서버를 바라보는 방식이다. 터널링이라고 부르기도 한다.

    중개서버방식이다

    OpenWRT가 실행되면서 중개 서버에 웹서버에 연결할 수 있는 터널을 생성하고, 그 링크를 저장한뒤, 클라이언트가 연결 요청을하면 중개하는 것이다.

    시도 1. 상용 ngrok 적용하기

    이러한 서비스를 제공해주는 상용 솔루션으로는 anydesk, ngrok 등이 있다. anydesk의 경우 타겟으로 잡은 NAT 장비의 칩셋과 호환되지 않았다.  남은 것은 ngrok이었고 다행이 우리의 칩셋과 호환되는 바이너리를 제공해주었다.

     

    그러나, 최신 버전의 OpenWRT에서 동작하지 않았고, 수년 전의 구버전에서 동작하였다. 구버전을 쓰려고 하면 못쓸 것은 없었지만, 신버전의 기능들이 매우 편리했으므로 신버전에서 동작시킬 방법을 구상하였다. 발생한 에러 메시지는 illegal instruction이었다. 주로 cpu가 제공해주는 명령어 셋과 프로그램의 빌드 대상이 맞지 않을 경우 나오는 문제였다. 그러나 이것만으로 확신할 수 없어서 GDB를 이용하여 다시 분석을 하였다. 

     

    문제가 발생한 지점은 go 런타임을 못찾는 것이었다. ngrok은 go로 빌드된 서버와 클라이언트로, 실행 시에 go 런타임을 요구한 것이다. 요구하는 go 런타임을 찾아서 넣어줬으나, go의 소스 코드 중에 변수의 타입을 float32로 지정하는 부분에서 다시 오류가 생겼다. 타입 관련 문제이므로 명령어 셋이 맞지 않는 것인가... 버전이 문제인가... 하고 추측할 거리는 있었으나, 지식의 부족과 검색의 한계로 더 이상 나아갈 수 없었다.

    시도 2. 구 버전 ngrok(오픈소스) 적용하기

    시선을 돌려, ngrok이 실행되는 구버전의 OpenWRT를 분석하였다. 구버전의 OpenWRT에서는 go 런타임 문제가 발생하지 않았다. 그렇다면, go 런타임은 표면적인 문제이고 그 내부엔 다른 문제가 아닐까 생각하였다.계속된 검색 및 분석을 하는 도중에 상용 ngrok의 오픈소스 시절의 github를 발견할 수 있었다.

     

    수년 전에 업데이트가 끝났지만, 더이상 앞으로 나아갈 수 없었던 상태이므로 해당 소스를 받아 빌드를 하였다. 빌드 도중에 역시 환경 설정, 경로 설정 등 문제가 발생하였으나, 해결하고 빌드를 성공할 수 있었다. 그런데, ngrok서버를 실행하려면 도메인이 필요했다. 내부에서 openssl을 이용하여 https까지 지원하였기 때문이다. AWS에서 개인적으로 구입하고 서버 역시 따로 AWS에 올려서 실행하였다. 서버는 동작을 잘하였으나, 클라이언트는 동작을 하지 않았다. 

     

    클라이언트가 동작하지 않는 이유는 상용 ngrok 클라이언트와 동일했다. go 런타임을 요구했고, 특정 변수를 float32로 지정하는 부분에서 문제가 발생하였다. go가 아닌 다른 언어로 빌드할 수 있는 ngrok이 있는지 찾기 시작했다. 다행히 c버전의 ngrok 클라이언트가 있었다.

     

    c버전의 ngrok 클라이언트는 다행이 최근까지 업데이트를 하고 있는 프로젝트였다. 해당 소스를 받아 빌드를 시도하였다. 역시 한 번에 되는 경우가 없다... 경로 설정 및 툴체인의 경로를 모두 수정하고, include 하는 라인을 수정해주어 빌드에 성공했다.

     

    마침내, 최신 버전의 OpenWRT에서 성공하는 것을 확인하였다. 혹시 몰라 장비의 펌웨어를 다시 초기화하고 ngrok-c 클라이언트를 실행했고 역시 성공하는 것을 알 수 있었다. 호스트(OpenWrt)위에 ngrok 클라이언트가 실행되고 이것이 ngrok 서버를 바라보고 있다. 이제 이 호스트에 접속하려는 클라이언트는 호스트를 직접 보는게 아니라 ngrok 서버를 바라보면 된다.

     

    3월 부터 괴롭히던 이슈를 마침내 해결했다. 개발에 참여하면서 가장 어려웠던 경험이었으나, 해결할 수 있었음에 감사한다.

    참고

    '문제해결' 카테고리의 다른 글

    mipsel32 개발 환경 구축  (0) 2021.12.09
    VMWare기반 개발 환경을 호스트 OS로 옮겼다.  (0) 2021.10.21
    ESP32 write cycles의 중요성  (0) 2021.10.06
    도커를 도입했다.  (0) 2021.08.15
    도커 도입의 필요성  (0) 2021.08.03

    댓글

Designed by Tistory.