Android 기기를 통해 연결된 PC 특정 Port로 터널링 하기

입질쾌감 물때표

안드로이드 기기를 연결한 PC에 특정포트를 외부로 터널링하는 과정을 기술함

아래의 이해를 위해 필요한 지식

  • 포트포워딩 (네트워크에 관한 상식적인? 지식 등)
  • SSH 커맨드 기능
  • 터널링의 개념
  • adb 커맨드 사용법
  • curl의 사용법

인터넷이 안되는 PC에 ReactJS으로 웹서비스를 로컬로 하나 돌리고 있다고 치자. PC에서는 http://localhost:3000으로 접속하면서 자신의 PC 브라우저로 개발을 열심히 하고 있는 상황이다.

PC에서 개발용 서버가 동작되고 있는 3000포트를 외부 정상 도메인을 이용하여 http://outside.com:33080 처럼 접속할수 있게 해주는 과정을 설명하는 것이다.

PixelExperience가 설치된 루팅 Android가 필요

참고 : https://webterror.net/2023/07/14/2037/
>> 루팅 안드로이드를 만드는 과정을 설명해놨음
>> 안드로이드에 termux를 이용해 apache2를 설치해 놓고 테스트용으로 이용해보자

우선적으로 로컬에서 실행되고 있는 3000포트를 안드로이드 기기로 포트포워딩을 시켜야 한다. 아래의 커맨드로 포트포워딩을 시키자. PC의 3000포트를 안드로이드의 3001 포트로 포워딩 시키는 예제이다.

adb 포트 포워딩

위의 내용에 해당하는 포워딩 커맨드

# 먼저 포트포워딩이 동작되게 해야 하므로 커널에 포워딩을 사용하는 것을 알려야 한다.
$ adb shell
$ sysctl net.ipv4.ip_forward=1 # 안드로이드 shell에서 입력해야 함
$ adb reverse tcp:3001 tcp:3000 # 첫번째가 안드로이드 자신, 두번째가 접속된 PC
>> adb로 android의 local 3001포트로 들어오는 패킷을 PC(안드로이드 입장에서는 remote)의 3000포트로 내보내줘라는 의미. adb 커맨드 실행 시점에서는 안드로이드에 들어오는 패킷을 거꾸로 remote(PC)에다가 보내는 것이라 reverse로 쳐야 한다.
# 기타 forward 등의 사용법
$ adb forward tcp:8000 tcp:8000

# 전체 삭제 방법
$ adb forward --remove-all
$ adb reverse --remove-all

위의 방법대로 하면 이제 포워딩이 실제로 되고 있는 것인지 테스트해 볼수 있다. adb로 접속한후 curl -v http://localhost:3001로 확인해 보자

% adb shell
lavender:/ # sh /sdcard/termux.sh                                              
/sdcard/termux.sh[21]: resize: inaccessible or not found
~ # 
~ # 
~ # curl -v "http://localhost:3001"
*   Trying 127.0.0.1:3001...
* Connected to localhost (127.0.0.1) port 3001 (#0)
> GET / HTTP/1.1
> Host: localhost:3001
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 200 OK
< cache-control: no-store, must-revalidate
< x-powered-by: Next.js
< etag: "s0obyte9m01kg"
< content-type: text/html; charset=utf-8
< content-length: 2142
< vary: Accept-Encoding
< date: Fri, 14 Jul 2023 14:21:26 GMT
< keep-alive: timeout=5
< connection: close
< 
<!DOCTYPE html><html><head><style data-next-hide-fouc="true">body{display:none}</style><noscript data-next-hide-fouc="true"><style>body{display:block}</style></noscript><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1689344486929"></script><script src="/_next/static/chunks/webpack.js?ts=1689344486929" defer=""></script><script src="/_next/static/chunks/main.js?ts=1689344486929" defer=""></script><script src="/_next/static/chunks/pages/_app.js?ts=1689344486929" defer=""></script><script src="/_next/static/chunks/pages/index.js?ts=1689344486929" defer=""></script><script src="/_next/static/development/_buildManifest.js?ts=1689344486929" defer=""></script><script src="/_next/static/development/_ssgManifest.js?ts=1689344486929" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript></head><body><div id="__next"><div class="text-center bg-slate-700 text-white">BrowserView</div><div class="container mx-auto px-4 pb-20"><h1 class="text-3xl font-bold text-center h-20 flex justify-center items-center">테스트용</h1><div class="flex flex-col space-y-3"><h2 class="font-bold">WebKit 테스트</h2><button class="btn">네이티브통신테스트</button><button class="btn">로그인</button><button class="btn">위치권한요청</button><button class="btn">카메라권한요청</button><span class="whitespace-pre-wrap"></span><h2 class="font-bold">강제 테스트</h2><button class="btn">네이티브통신테스트</button><button class="btn">로그인</button><button class="btn">위치권한요청</button><span class="whitespace-pre-wrap"></span></div></div></div><script src="/_next/static/chunks/react-refresh.js?ts=1689344486929"></script><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","n* Closing connection 0
~ #

잘 접속되는 것을 확인 !

Android SSH > outside.com 외부서버로의 터널링

먼저 위의 연결과는 별개로 Android와 outside.com 서버와의 연결이 포트포워딩이 되는지 테스트를 위해 안드로이드에 서비스되고 있는 apache2의 8080포트를 outside.com 서버에 연결해서 붙여보자. 아래의 커맨드로 연결하면 된다.

$ apachectl start # 아파치 기동
$ curl -v "http://localhost:8080" # 서버 응답 테스트
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.1.2
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 14 Jul 2023 14:33:30 GMT
< Server: Apache/2.4.57 (Unix)
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html
< 
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host localhost left intact
$ # 외부망으로 터널링 접속
$ ssh -fCNR 3380:localhost:8080 user@outside.com -o GatewayPorts=yes

SSH 터널링이 되었으면 outside.com의 shell로 들어가서 3380으로 접속하면 응답을 하는지 확인해 보자

$ ssh user@outside.com
.... 접속

테스트를 하기전에 아래의 커널에 route_localnet를 사용한다고 알려야 한다.
[user@outside.com]# echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet

[user@outside.com]# curl -v "http://localhost:3080"
*   Trying 127.0.0.1:3080...
* Connected to localhost (127.0.0.1) port 3080 (#0)
> GET / HTTP/1.1
> Host: localhost:3080
> User-Agent: curl/8.0.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Fri, 14 Jul 2023 14:50:25 GMT
< Server: Apache/2.4.57 (Unix)
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html
< 
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host localhost left intact

연결테스트를 했더니 잘되는 것을 확인할수 있다. 그럼 이제 인터넷이 되는 또 다른 휴대폰으로 http://outside.com:3080으로 접속을 해보자.

해보면 알겠지만 위와 같이 접속해도 연결이 되지 않는다. 이것 때문에 outside.com 서버에서 해줘야 할것이 있다.

[user@outside.com]# iptables -t nat -A PREROUTING -p tcp --dport 33080 -j DNAT --to-destination 127.0.0.1:3080

위의 iptables로 PREROUTING 체인을 걸어서 외부의 다른 포트로 들어온 경우 localhost의 3080으로 도착하도록 연결해줘야 한다. 위의 체인을 걸고 다시 브라우저에서 접속을 해보면!!

인터넷이 되는 다른 전화기로 브라우저로 접속 : http://outside.com:33080

아파치에서 응답하는 위의 문구를 확인할수 있을 것이다.!!
이제 다했다.

$ ps aux | grep ssh # 이미 커넥션이 터널링이 된 세션을 날려줘야 한다. 해당 프로세스 번호를 kill 해준다.
$ kill -9 번호

이제 Android에서 포워딩 되고 있는 3001 포트를 outside.com의 외부 서버로 SSH 접속을 통해 양쪽을 다시한번 연결해 준다. 연결되는 내용은 안드로이드에서 ssh로 outside.com에 접속할때 터널링 옵션으로 localhost의 3001번을 outside.com의 외부서버의 3380 포트로 연결할 것이다.

$ ssh -fCNR 3380:localhost:3001 user@outside.com -o GatewayPorts=yes

옵션에 관한 내용은 알아서 검색해서 찾아보시길, 위와 같은 옵션을 주면 터널링할때 3380:localhost:3001 관계가 게이트웨이의 역할로 되면서 패킷을 전달만해주는 역할을 하게 된다.

이후 위에서 아파치 페이지 연결 테스트를 한 것과 동일하게 outside.com 서버에서 동일한 처리를 해주면 이제 외부에서 outside.com의 서버를 거쳐서 안드로이드를 경유하고 인터넷이 안되는 PC의 Apache 서버에 도착할수 있게 된다.

[user@outside.com]# echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
[user@outside.com]# iptables -t nat -A PREROUTING -p tcp --dport 33080 -j DNAT --to-destination 127.0.0.1:3080

누군가에게 도움이 되길 바라며…
빠진 내용이 있으면 다시 보충하겠다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다