SSH 터널링

개요

SSH 터널링이란 ssh 프로토콜을 통해 데이터를 주고 받는 모든 일련의 과정을 말한다.

터널링

터널을 뚫는다는 표현에는 두 가지 뜻이 담겨있다.

  • 실질적으로 stateful한 상태로 소켓이 열려 있는 상태
  • 열린 소켓 포트를 통해 행위가 일어남
    Contents
    사실 그래서 그냥 단순한 SSH#원격 접속 자체도 사실 크게 보면 터널링이기는 하다.

그러나 여기에서 말하는 터널링은 조금 더 국한된 의미로, 뚫린 터널을 통해 다른 네트워크 통신을 가능하게 하는 것을 말한다.
Pasted image 20240712235104.png
이 그림이 가장 직관적으로 터널링을 나타낸다고 생각한다.
원래는 내부망으로만 접근 가능한 맨 뒷단의 웹서버에 접근할 수 없다.
그러나 ssh 터널을 뚫으면 암호화된 형태로 요청을 주고받을 수 있게되는 것이다.

이때 중간자 역할을 해주는 서버를 흔히 중계 서버, 바스티온 서버라고 부른다.
위의 그림은 세 개의 컴퓨터의 관계를 그렸지만, 방화벽이 걸린 한 컴퓨터가 ssh만 열어준다면 방화벽으로 막히는 모든 요청을 보낼 수 있게 된다.

장점

실습을 하기 이전에, 간단하게 장점을 짚어보자.

필요성

잠시 이게 필요한 이유를 더 어필해보려고 한다.
내 로컬에서 접근할 수 없는 내부 서버의 로그를 확인하거나 조작하고 싶다.
가장 엔지니어스러운 방법은 당연히 터미널을 조작하는 것이고 그렇다면 ssh 연결이 아무래도 좋다.
그런데 접근할 수 없다.. 그러면 어떡하지?
중계 서버를 이용할 수 있겠다.

중계서버에 먼저 ssh 연결을 한다.
그리고 이 중계서버의 터미널에서 내부 서버에 ssh 연결을 한다.
그러기 위해서는 중계 서버에 내부서버에 연결되기 위한 ssh 키가 존재해야만 한다.
달리 말해, 중계 서버가 털리면 내부 서버의 키도 탈취당하게 된다.
하지만 터널만 뚫어놓는 것이라면, 키는 내 로컬에서 관리하면서 접근하는 것이 가능해진다.
이것을 [[#실습]]에서 보게 될 것이다.

종류

설정 방법에 따라 세 가지로 나뉜다.
이 중에서 다이나믹 터널링은 조금 특별하다.

로컬 터널링

내 클라이언트에서 바스티온 서버에게 터널링을 뚫어달라고 요청한다.

ssh -L ([로컬 ip]:)[로컬 포트]:[원격지 ip]:[원격지 포트] \
	[SSH 서버 username]@[SSH 서버 ip]

길어서 헷갈릴까봐 줄나눔을 했다.
기본적으로는 ssh 연결을 시도한다.
다만 -L ([로컬 ip]:)[로컬 포트]:[원격지 ip]:[원격지 포트]가 추가된다.

보통 사용방식을 예를 들어보겠다.

ssh -L 2222:private.com:80 public.com:22

이게 무슨 뜻인가?

  1. public.com의 ssh 서버에게 터널을 뚫어달라 요청한다.
  2. 그 터널은 내 로컬에서는 2222번 포트에 연결되어 있게 하겠다.
  3. 내가 내 로컬의 2222번 포트에 접근하면, 너가 만든 터널을 통해 private.com의 80번 포트로 연결되게 해달라

그래서 이제부터는 내가 내 로컬의 2222번 포트에 요청을 보내면, 그것은 암호화된 채로 바스티온 서버로 전달되고, 바스티온 서버는 그 요청을 private.com로 보내버린다!

리모트 터널링

바스티온 서버에서 내 클라이언트에게 터널링을 뚫겠다고 제안한다.

ssh -R [원격지 포트]:[대상서버 ip]:[대상서버 포트] [원격지 서버 username]@[원격지 서버ip]

방금의 거꾸로의 과정이다.
대신 이번에는 바스티온 서버 쪽에서 내 로컬로 터널을 뚫는 명령어를 입력하는 것이다.
서버 클라이언트 구조로 이뤄진 ssh이라는 것을 유의하자.
이번에는 바스티온 서버의 ssh 클라이언트가 내 클라의 ssh 서버에 요청을 보내는 과정이다. 이를 유의하도록 한다.

다이나믹 터널링

위의 두 터널링은 사용법을 보면 대충 감이 오겠지만, 터널링을 통해 통신하게 될 포트가 명시되어 있다.
만약 내가 80번 포트를 뚫었다면 나는 꼼짝없이 HTTP 통신만 가능한 격이다.
그렇지만 이 동적 터널링은 타겟의 포트 따위 지정하지 않는다..
즉, 터널을 뚫어만 두고, 이 터널을 통해 들어가는 모든 요청을 처리한다.

이 터널링은 SockS 프로토콜의 실체를 담당한다.
따로 프로토콜을 지정해서 말하지만 결국 원리 자체는 ssh 암호화가 기반일 뿐인 듯하다.

실습

시나리오

상황

원격 서버 A,B가 있다.
A 서버는 내 로컬에서 22번 포트로 ssh 연결이 가능하다.
B 서버는 A서버와 같은 공인 IP가 없으며 사설 망으로 A 서버와 통신한다.
A,B 서버 모두 80번 포트에 웹서버가 띄워져 있으나, 방화벽이 존재하여 내 로컬에서 접근할 수 없다.

목표

A 서버의 웹서버에 http 요청을 보낸다.
B 서버의 웹서버에 http 요청을 보낸다.
B 서버에 ssh 연결을 한다.

실습 준비

4.RESOURCE/KNOWLEDGE/AWS/AWS를 통해 실습을 진행한다.
Pasted image 20240713111809.png
먼저 작고 귀여운 Amazon Virtual Private Cloud 세팅부터.
Pasted image 20240713111604.png
퍼블릭 서브넷 쪽은 ip 세팅을 원활하게 하기 위해 자동 할당 설정을 들어가서 꼭 해주도록 한다.
아니 이거 왜 귀찮게 처음부터 세팅 옵션 안 넣어주는 거임;;

#!/usr/bin/bash
dnf install -y nginx
systemctl start nginx
systemctl enable nginx

다음으로 인스턴스를 띄우는데, 웹서버 예시를 위해서 Nginx를 설치해주도록 하겠다.
Pasted image 20240713115823.png
여기까지 됐으면 준비는 완료.
퍼블릭 하나, 프라이빗 하나.
다른 컴퓨터라는 것을 명확히 하기 위해 둘의 암호화는 다른 키로 진행했다.
같은 vpc 안이라 게이트웨이가 없더라도 통신은 가능하다.

짤막 네트워크 정리

같은 내부 망(vpc, 혹은 서브넷)에 있다면 둘은 통신이 가능하다.
네트워크 인터페이스에 이미 정보가 명시되어 있기 때문이다.
이들의 통신은 ARP 프로토콜을 통해 이뤄진다.
간단하게 말하면, arp는 같은 대역으로 표기된 망에 브로드캐스트로 상대 mac 주소를 알아내는 행위다.
이 과정을 거치면 상대 mac 주소가 나오게 되고 내 컴퓨터는 mac 주소 정보를 담은 전기 신호를 날릴 것이다.
그러면 허브, 혹은 스위치라는 장비가 이것을 받아서 정확한 mac 주소에 해당하는 컴퓨터에 신호를 보내주게 된다.

만약 다른 네트워크로 가고 싶다면 어떻게 하지?
라우터를 이용한다.
내 컴퓨터는 DHCP 프로토콜을 통해 사설 ip 주소를 배정받기도 하지만, 동시에 라우터의 위치를 알게 되기도 한다.
내 컴퓨터 기준에서 라우터의 위치를 바로 게이트웨이라고 한다.

Pasted image 20240713124049.png
개인키는 자신 말고 권한을 아무도 가져서는 안 된다.
Pasted image 20240713124223.png
퍼블릭 서브넷에 위치한 인스턴스에 공인 ip를 통해 접근해 보았다.
내부에서는 웹서버도 잘 동작되는 것이 확인된다.
Pasted image 20240713124427.png
하지만 해당 인스턴스는 ssh 연결만 가능하지 그 외의 어떤 것도 불가능하다.
Pasted image 20240713124518.png
프라이빗 쪽은 애초에 공인 ip도 없다.

참고?

다른 글들을 찾아보면 /etc/ssh/sshd_config 파일에서 다음의 부분들을 수정해줘야 한다고 말한다.
Pasted image 20240710184334.png
먼저 중계 서버의 sshd 설정을 바꿔준다.
포워딩이 가능해야 하고, 게이트웨이 포트를 열어주는 것을 허용한다.
(사실 gatewayports 옵션은 무슨 뜻인지 잘 모르겠다.)

그런데 여태까지 테스트해본 바로는 이 설정 안 해도 상관 없이 잘만 되더라.

로컬 터널링

Pasted image 20240713125241.png
위에서 사용한 방식으로 로컬 터널링을 뚫었다.
참고로 현재 열린 터미널은 바스티온 서버이고, 터널도 뚫으면서 터미널 연결도 해준 것이다.
터널링을 시험해보려면 다른 터미널을 열어야 해서 불편하게 느껴질 수도 있다.
그러나 이 터미널을 닫는 순간 터널은 사라진다.
즉, 터널을 뚫어서 사용자를 들여보내는 관리자 입장에서는 관리가 용이하다.
Pasted image 20240713125735.png
뚫은 터널은 퍼블릭 인스턴스의 80번 포트의 웹서버.
내가 내 로컬의 2222번 포트로 http 요청을 보내자, 들어갈 수 없었던 퍼블릭 인스턴스의 웹서버 응답이 돌아왔다.
참고로 글 내용물은 내가 일부러 보기 편하라고 수정했다.

리모트 터널링

E-로컬 ssh 서버 세팅을 먼저 해주어야 한다.
원격에서 내게 요청을 보내기 위해서는 내 ssh 서버가 먼저 있어야만 한다는 것이다.
Pasted image 20240713144213.png
바스티온 서버 쪽에서 먼저 요청을 날린다.
이번에는 프라이빗 인스턴스의 22번 포트로 연결되는 터널을 2222번 포트에 뚫어주겠다고 제안해본다.
Pasted image 20240713143940.png
엣..
이거 아까 2222번 포트에 대해 캐싱된 정보가 남아서 생기는 문제인데, 중간에 명령어를 따라치면 해결된다.
Pasted image 20240713144131.png
오케이!
ip 정보를 보면 10.0.2.224로 찍히는데,
Pasted image 20240713144347.png
과연.. 프라이빗 인스턴스로 잘 들어간 것이 확인된다.
아쉽게도 프라이빗이 인터넷으로 나가는 망이 없어서 당장은 웹서버가 설치되지 못 했다.

ami나, nat gateway를 이용하는 방법이 있는데 빠른 방법은 후자일 거라 생각한다.

Pasted image 20240713144815.png
실습 정리 중에 문제가.. 발생했다;;
리모트 터널을 연 프로세스가 꺼지지 않는다.
Pasted image 20240713144930.png
아묻따 죽여버리기
Pasted image 20240713145018.png
깔깔

다이나믹 터널링

프라이빗 인스턴스에 웹 서버 설치하기

Pasted image 20240713145806.png
일단.. nat gateway 만들고..
Pasted image 20240713145943.png
프라이빗 서브넷 쪽에 라우팅 테이블에 nat gateway를 명시해준다.
Pasted image 20240713150019.png
석세스
Pasted image 20240713150841.png
프라이빗 인스턴스 입장에서는 게이트웨이가 10.0.2.1에 뚫려 있는 게 확인된다.
그곳에 가상의 라우터가 있고, 나는 그 라우터의 테이블에 nat gateway를 설정한 것이다.

다이나믹하게 ssh, http 다 날리기

다이나믹 터널링은 모든 요청을 처리할 수 있다고 언급했다.
그러니 그게 가능하다는 것을 확인하기 위해 위에서 해본 두 가지를 다 해볼 것이다.
Pasted image 20240713151142.png
바스티온 서버에 대한 요청 자체는 참 간단하다.
그냥 2222 포트에 아무튼 터널 뚫을게~
대신 다음 요청부터가 조금 어려워진다.
이전까지는 1:1로 관계를 뚫었지만 이제는 우리가 포트도 지정할 수 있게 되었기 때문에 사용법이 달라진다.
기본적으로 이것이 바로 SockS 프로토콜에 해당하며 보낼 때도 SockS 프록시를 이용해야만 한다.
Pasted image 20240713152915.png
curl 요청에서 프록시를 사용하는 방법은 -x옵션을 붙이는 것이다.
스키마도 socks라고 명시를 해줬다.

ssh -o ProxyCommand="nc -x 127.0.0.1:1080 %h %p" [USER]@[PRIVATE_SERVER]

ssh에서 프록시를 사용하기 위해서는 ProxyCommand를 사용해야 한다.
내부 명령어는 원격의 원하는 주소와 포트에 이 주소로 접속하고자 한다~ 정도의 의미로만 생각하자.
Pasted image 20240713162718.png
그러면 이렇게 접속이 가능하다.
Pasted image 20240713162858.png
다이나믹 터널링으로 가능해진 동작.
curl과 ssh 명령이 한번에 실행 가능해진다.

참고