포트 포워딩의 종류
로컬 포트 포워딩
: 로컬 머신의 특정 포트에 들어오는 트래픽을 SSH를 통해 원격 서버(SSH 서버)의 특정 포트로 전달합니다.원격 포트 포워딩
: 원격 서버(SSH 서버)의 특정 포트로 들어오는 트래픽을 SSH를 통해 로컬 머신의 특정 포트로 전달합니다.동적 포트 포워딩
: 다양한 프로그램의 연결이 SSH 클라이언트를 통해 SSH 서버로 전달된 다음 대상 서버로 전달됩니다.
Local Port Forwarding
프라이빗 서브넷에 생성한 RabbitMQ 서버를 외부에서 접근할 수 있도록 설정이 필요할 때, 점프 서버를 통해서 로컬 포트 포워딩을 설정한다면 점프 서버의 공인 아이피로 RabbitMQ 서버의 MQTT 포트에 접근할 수 있습니다. 우선 로컬 포트 포워딩을 수행하려면 먼저 대상 또는 원격 호스트에 대한 SSH 액세스 권한이 있어야 합니다.
로컬 포트 포워딩에 아래 두가지 그림이 있는데요, 먼저 가장 첫번째 그림은 RabbitMQ 서버의 로컬호스트로 리슨하고 있는 1883포트를 도착지로 하고 클라이언트 서버의 로컬 호스트로 1883 포트를 노출할 때입니다. 마지막 그림은 위 사례를 그림으로 표현했습니다.
$ ssh -L localhost:1883:localhost:1883 devops@10.0.84.7
$ ssh -N -f -L 0.0.0.0:1883:10.0.84.7:1883 [devops@10.0.84.7](mailto:devops@10.0.84.7)
- L 플래그는 클라이언트 측에서 원격 서버 측으로 전달할 포트를 지정합니다.
- N 플래그는 SSH 실행 성공 이후에 SSH 터미널을 열지 않기 위해서 사용합니다.
- f 플래그는 백그라운드로 동작시키기 위해서 사용합니다.
게이트웨이 포트는 네트워크 트래픽이 한 네트워크에서 다른 네트워크로 넘어갈 때 사용하는 중간 지점의 포트로 트래픽을 받아서 적절한 목적지로 전달하는 역할을 하며 위 그림에서는 로컬에서 게이트웨이 포트를 열어줍니다. 위 그림에서는 점프 서버가 로컬이며 점프 서버의 공인 아이피를 통해서 클라이언트는 내부망에 존재하는 RabbitMQ의 MQTT 포트에 접근할 수 있게 됩니다.
하지만 만약 ssh 세션이 타임아웃으로 인해서 종료된다면 더 이상 외부에서의 트래픽이 도착지 로 전달되지 않을 수 있으므로 서버측 또는 클라이언트 측에 타임아웃이 끊기지 않도록 주기적으로 TCP 패킷을 전달하도록 설정해야 하며 저는 클라이언트측에 해당 설정을 적용하였습니다.
$ vi ~/ssh/config
...(중략)
Host rabbitmq
HostName 10.0.84.7
User devops
IdentityFile ~/.ssh/id_rsa
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 3
Port 22
서버에 30초 간격으로 TCP keep-alive 패킷을 전송하도록 하여 연결이 유휴 상태일 때도 유지되도록 하며 만약 서버가 응답하지 않을 경우 30초 간격으로 최대 3번까지 keep-alive 패킷을 보냅니다.
Remote Port Forwarding
원격 포트 포워딩은 위 사례에서 도착지와 출발지는 같지만 설정 주체가 서로 뒤바뀐다고 생각하면 좋을 것 같아요. 위 사례에서는 클라이언 측이 점프 서버로 인터넷에서 접근이 가능하고 RabbitMQ가 설치되어 있는 서버측이 프라이빗 서브넷에 위치한 결과 인터넷에서 접근 이 불가하여 로컬 포트 포워딩을 사용하여 점프 서버에 SSH 터널링을 적용해주었어요. 이번에는 RabbitMQ가 설치되어 있는 서버에서 터널링을 설정하여 점프 서버를 통해 RabbitMQ의 mqtt 포트로 접근하도록 설정해줄 것입니다. 이때는 RabbitMQ가 설치되어 있는 서버가 로컬이 될 거에요.
$ ssh -R 0.0.0.0:1883:localhost:1883 devops@10.0.85.6
각각 언제 로컬 포트 포워딩을 사용하고 원격 포트 포워딩을 사용할지에 대해서는 현재 로컬을 기준으로 원하는 대상으로부터 내 로컬에 접근할 수 있느냐로 볼 수 있을 것 같아요, 만약 특정 대상이 내 로컬에 접근이 가능하다면 로컬 포트 포워딩을 사용하고 내 로컬에 접근이 불가능하여 다른 서버를 통해서 들어와야만 한다면(우회해야 한다면) 원격 포트 포워딩을 사용할 수 있습니다. 여기서 로컬은 곧 내가 터널링 설정을 진행할 수 있는 서버를 의미하며 대상은 로컬 서버의 특정 포트로 접근할 수 있는 클라이언트를 의미합니다.
- 로컬 포트 포워딩: SSH Client → SSH Server
- 원격 포트 포워딩: SSH Server → SSH Client
Dynamic Port Forwarding
로컬 포워딩과 원격 포트 포워딩은 모두 로컬과 원격 포트를 정의해야 하며 해당 포트로의 릴레이만 제공하지만 동적 포트 포워딩은 들어오는 모든 트래픽을 리모트 서버로 전달할 연결 포트를 동적으로 지정하여 사전에 알 수 없는 포트가 있거나 많은 포트를 릴레이해줘야 할 경우 유용하게 사용할 수 있습니다.
현재 NCP의 관리형 쿠버네티스 서비스인 NKS를 사용하고 있는데 API 서버에 접근할 수 있는 서버를 바스티온 서버 하나로 제한하고 있어요. 저는 API 서버에 명령을 내리기 위해서 바스티온 서버에 SSH 접근을 한 뒤에 kubectl 커맨드를 사용할 수 있지만 제 로컬에서 API 서버에 명령을 내릴 수 있기를 원하기에 SOCKS5 프록시를 사용해야만 했습니다.
먼저 바스티온 서버의 SSH 설정을 ~/.ssh/config에 설정하고 쿠버네티스 가이드에 나와있는 것과 같이 동적 포트 포워딩을 사용했어요.
$ ssh -D 1080 -q -N fleet-dev-bastion
$ export HTTPS_PROXY=socks5://localhost:1080
$ k config get-contexts
참고자료