Cloud Modernization 20
프라이빗 서브넷: 안전한 외부 통신의 비밀
프라이빗 서브넷은 인터넷을 못 쓰는 곳이라고만 생각하면 패치 다운로드 같은 outbound 통신을 어떻게 처리하는지 막힌다.
근거 · 교안 p82-p84
1장: 프라이빗 서브넷, 정말 인터넷이 안 되나요?
솔라의 손가락이 모니터 위를 맴돌았다. 직접 그린 간단한 클라우드 아키텍처 다이어그램이었다. 네모난 상자 몇 개와 화살표 몇 개가 전부였지만, 그중 ‘프라이빗 서브넷’이라고 이름 붙인 상자 앞에서 생각이 턱 막혔다. 화면의 푸른빛이 솔라의 막막한 표정을 비췄다.
“언니, 잠깐 이것 좀 봐줄 수 있어?”
노트북을 들고 소파에 앉아 있던 루나가 고개를 들었다.
“프라이빗 서브넷 말이야. 외부 인터넷에서 직접 접근할 수 없도록 격리된 공간. 그래서 중요한 데이터베이스나 내부 애플리케이션을 두는 곳. 내가 이해한 게 맞지?”
솔라는 자신이 그린 다이어그램의 ‘프라이빗 서브넷’ 상자를 톡톡 두드리며 말했다. 당연한 사실을 확인하는 듯한 목소리였지만, 확신보다는 무언가에 부딪힌 사람의 어조에 가까웠다.
“응, 맞아. 외부에서는 그 안을 들여다볼 수도, 먼저 말을 걸 수도 없는 폐쇄된 공간처럼 설계하지.”
루나의 차분한 대답에 솔라는 기다렸다는 듯이 미간을 좁혔다.
“바로 그거야! 폐쇄된 공간. 그런데 내가 본 자료에는 ‘프라이빗 서브넷은 인터넷에 간접적으로 액세스를 허용함’이라는 문장이 있었어. 이건 모순 아니야? ‘간접적으로’라는 건 결국 안 된다는 말을 어렵게 하는 것뿐이잖아. 외부랑 단절시키려고 만든 건데 왜 자꾸 인터넷 접근 이야기를 하는 건지 모르겠어.”
솔라는 ‘간접적으로’라는 단어가 마치 인쇄 오류인 것처럼 느껴졌다. 보안을 위해 굳게 닫아 건 대문에 ‘사실 작은 쪽문이 있음’이라고 적어둔 셈이니까.
루나는 잠시 솔라의 다이어그램을 들여다보더니, 화면 속 프라이빗 서브넷 상자를 손가락으로 가리켰다.
“그럼 이렇게 생각해 보자. 솔라 네가 지금 설계하는 이 프라이빗 서브넷 안에 아주 중요한 서버가 한 대 있다고 상상해 봐. 예를 들면, 우리 회사 전체의 고객 정보가 담긴 데이터베이스 서버 같은 거.”
“응, 상상했어. 절대로 외부에 노출되면 안 되는 서버.”
솔라는 고개를 끄덕였다. 당연한 가정이었다.
“좋아. 그 서버는 하드웨어일 뿐만 아니라, 운영체제도 있고 여러 소프트웨어도 설치되어 있겠지. 시간이 흐르면 그 서버에게는 어떤 일이 일어날까? 그냥 그대로 두기만 해도 괜찮을까?”
루나의 질문은 기술적인 정의가 아닌, 시간의 흐름에 관한 것이었다. 솔라는 잠시 생각에 잠겼다. 매일같이 자신의 노트북 화면 구석에 뜨는 알림 창이 떠올랐다.
“아… 보안 패치나 시스템 업데이트가 필요하겠네. 새로운 취약점이 발견될 수도 있으니까.”
“그렇지. 그럼 그 중요한 보안 패치 파일은 보통 어디에 있지? 우리가 어디서 다운로드해야 할까?”
“……인터넷.”
솔라의 입에서 나온 대답은 너무나 명백해서 허탈할 정도였다. 동시에, 스스로 만들어낸 논리의 벽에 부딪혔다. 외부로부터의 완벽한 보호. 그리고 인터넷을 통한 필수적인 업데이트. 이 두 가지는 양립할 수 없어 보였다.
“어? 근데 그러면 안 되잖아! 프라이빗 서브넷 안에서 어떻게 인터넷에 접속해? 업데이트 서버에서 파일을 받아오는 건 결국 외부에서 내부로 들어오는 통신이잖아. 그렇게 문을 열어두면 해커가 그 길로 들어올 수도 있는 거 아니야?”
스스로 질문하고 스스로 반박하던 솔라는 순간 말을 멈췄다.
“아니, 잠깐만. 반대로 생각하면… 보안 업데이트를 제때 못 받아서 서버가 점점 더 위험해지는 거잖아? 그것도 말이 안 되는데.”
완벽한 고립은 곧 완벽한 방치였다. 최신 위협에 무방비로 노출된 채 낡아가는 서버의 모습이 그려졌다. 철통 보안을 위해 만든 감옥이, 오히려 가장 위험한 공간이 되어버리는 역설. 솔라는 그제야 자신이 놓치고 있던 조각을 깨달았다. 다시 모니터 위로 시선을 돌렸다. 아까는 그저 모순 덩어리로 보였던 문장이 새롭게 다가왔다.
프라이빗 서브넷은 인터넷에 ‘간접적으로’ 액세스를 허용함.
“아!”
솔라는 짧은 탄성을 뱉었다. ‘간접적으로’라는 단어가 더 이상 오류나 말장난으로 보이지 않았다. 그것은 포기가 아니라, 문제 해결의 실마리였다.
“그래서 ‘간접적으로’였구나. 인터넷 연결을 완전히 차단하는 게 아니라… 안에서는 밖으로 나갈 필요가 분명히 있다는 뜻이었네. 다만, ‘직접’이 아닐 뿐.”
고립과 단절이라는 단어에 갇혀 있던 생각이 한순간에 확장되는 느낌이었다. 프라이빗 서브넷의 목적은 무조건적인 차단이 아니라 ‘안전한 통신’이었다.
“이제 알겠어. 필요할 땐 밖으로 나갈 수 있어야 해. 하지만 외부에서 마음대로 들어오게 해서는 안 돼. 정확히 이 두 가지를 모두 만족시켜야 하는 거였어.”
솔라의 목소리에 활기가 돌았다. 막혔던 길이 뚫린 기분이었다. 하지만 새로운 길이 나타나자 곧 새로운 질문이 고개를 들었다.
“그럼… 그 ‘간접적으로’ 통신한다는 게 정확히 어떤 원리야? 어떻게 우리 서버가 시작한 요청은 허용하면서, 외부에서 불쑥 들어오는 요청은 막을 수 있는 거지?”
2장: NAT 게이트웨이: 내부 서버의 외부 요청 도우미
솔라의 질문이 채 끝나기도 전에, 루나는 솔라의 노트북 옆에 놓여 있던 태블릿을 들어 화면을 켰다. 거기에는 아까 솔라가 그렸던 단순한 다이어그램이 복제되어 있었지만, 몇 가지 요소가 추가되어 있었다. 프라이빗 서브넷 상자 옆에는 ‘NAT 게이트웨이’라는 새로운 상자가 생겼고, VPC(가상 사설 클라우드) 전체를 감싸는 테두리 밖으로는 ‘인터넷 게이트웨이’와 구름 모양의 ‘인터넷’이 그려져 있었다.
루나는 말없이 프라이빗 서브넷 안의 서버 그림에서 시작해, 새로 생긴 NAT 게이트웨이를 거쳐 인터넷 게이트웨이로 향하는 점선 화살표를 그렸다. 그 화살표는 인터넷을 지나 ‘외부 업데이트 서버’라는 상자를 가리키고 있었다. 솔라는 이전보다 복잡해진 그림을 보며 자신이 던졌던 마지막 질문을 떠올렸다. ‘안에서 시작한 요청은 허용하고, 밖에서 불쑥 들어오는 요청은 막는 원리.’ 이 새로운 상자들이 바로 그 원리의 열쇠인 것 같았다.
“이 NAT 게이트웨이라는 게 그 ‘간접적으로’ 통신하게 해주는 장치구나. 우리 서버의 요청을 대신 전달해주는 문지기 같은 건가?”
솔라가 새로운 다이어그램을 손가락으로 짚으며 말했다.
“비슷해. 문지기보다는… 주소를 잠시 바꿔주는 대리인에 가깝지.”
루나는 솔라가 그린 프라이빗 서브넷 안의 서버를 가리켰다.
“자, 이 서버가 보안 패치를 다운로드해야 한다고 해보자. 이 서버의 주소는 내부에서만 통용되는 사설 IP 주소야. 예를 들어 10.0.1.5라고 해볼까? 이 주소는 인터넷에서는 아무 의미가 없어. 이 주소로 편지를 보내도 배달될 곳이 없지.”
“응, 그건 알겠어. 그래서 외부에서 직접 접근할 수 없는 거고.”
“바로 그거야. 이제 이 10.0.1.5 서버가 외부에 있는 업데이트 서버(203.0.113.10이라고 하자)에게 ‘최신 패치 파일 주세요’라는 요청 패킷을 보낸다고 상상해 봐.”
루나는 솔라에게 펜을 건네며 태블릿 위를 가리켰다.
“솔라, 네가 직접 이 요청의 여정을 따라가 볼래? 시작은 10.0.1.5 서버야.”
솔라는 펜을 들고 10.0.1.5 서버에서 출발하는 화살표를 그리기 시작했다. 프라이빗 서브넷의 라우팅 테이블에 따라, 인터넷으로 향하는 모든 트래픽은 NAT 게이트웨이로 향하도록 설정되어 있었다. 솔라의 화살표는 자연스럽게 NAT 게이트웨이 상자에 닿았다.
“일단 NAT 게이트웨이까지는 갔어. 그런데 이 다음이 문제야. 이 요청 패킷의 출발지 주소는 여전히 10.0.1.5잖아. 이 주소로는 인터넷에서 응답을 받을 수가 없는데?”
바로 그 지점이었다. 솔라가 막연하게 마법처럼 연결될 것이라 생각했던 부분의 구체적인 작동 방식.
“거기서 NAT 게이트웨이가 자기 일을 해.”
루나가 말했다.
“NAT 게이트웨이는 요청 패킷을 받아서, 출발지 IP 주소를 10.0.1.5에서 자기 자신의 공인 IP 주소로 바꿔치기해. 예를 들어 NAT 게이트웨이의 주소가 52.78.100.200이라면, 이제 패킷의 출발지는 52.78.100.200이 되는 거야.”
솔라의 눈이 커졌다.
“아! 주소를 바꿔버리는구나! 우리 서버의 진짜 주소는 숨기고, 자기가 보낸 것처럼 위장하는 거네?”
“정확해. 그리고 NAT 게이트웨이는 이 변환 기록을 잠시 테이블에 저장해둬. ‘10.0.1.5가 보낸 요청을 내가 52.78.100.200으로 바꿔서 보냈음’ 하고 말이야. 이제 주소가 바뀐 패킷은 인터넷 게이트웨이를 통해 당당하게 인터넷으로 나가서, 목적지인 업데이트 서버 203.0.113.10에 도착하지.”
솔라는 루나의 설명을 들으며 화살표를 계속 그어 나갔다. NAT 게이트웨이에서 나온 화살표는 인터넷 게이트웨이를 지나 외부 업데이트 서버에 닿았다. 이제 요청은 성공적으로 전달되었다.
“그럼 응답은? 업데이트 서버는 패치 파일을 다시 보내줄 텐데, 어디로 보내줘?”
“요청 패킷의 출발지가 어디였지?”
“NAT 게이트웨이의 공인 IP 주소!”
솔라가 외쳤다. 수수께끼가 풀리는 순간이었다. 업데이트 서버는 자기가 받은 요청의 출발지, 즉 NAT 게이트웨이의 공인 IP로 응답을 보낸다. 응답 패킷은 인터넷을 거쳐 NAT 게이트웨이에 도착한다.
“이제 NAT 게이트웨이가 아까 기록해둔 테이블을 볼 차례야. ‘어, 이 응답은 내가 10.0.1.5를 대신해서 보냈던 요청에 대한 답이네?’라고 확인하는 거지. 그리고 응답 패킷의 목적지 주소를 다시 원래 서버의 사설 IP인 10.0.1.5로 바꿔서 프라이빗 서브넷 안으로 들여보내 주는 거야.”
솔라는 마지막 화살표를 그었다. NAT 게이트웨이에서 출발한 화살표는 정확히 10.0.1.5 서버를 향했다. 밖으로 나갔던 요청이 안전하게 응답을 가지고 돌아온 완벽한 여정이었다. 외부 인터넷은 단 한 번도 10.0.1.5라는 주소를 본 적이 없었다. 그들이 소통한 대상은 오직 NAT 게이트웨이뿐이었다.
“이거였구나… ‘간접적으로’라는 말의 의미가. 우리 서버가 직접 인터넷에 나서는 게 아니라, NAT 게이트웨이라는 대리인을 내보내는 거였어. 요청을 시작한 주체는 우리 서버지만, 외부 세상과 얼굴을 마주하는 건 NAT 게이트웨이인 셈이네.”
솔라는 자신이 그린 화살표의 흐름을 보며 고개를 끄덕였다. 막연했던 ‘IP 주소 변환’이라는 개념이 이제는 선명한 그림으로 보였다. 아웃바운드 통신이 어떻게 가능한지 완벽하게 이해됐다. 하지만 그림을 완성하고 나자, 새로운 의문이 고개를 들었다.
“잠깐만, 언니. 그럼 이상한데. NAT 게이트웨이는 어쨌든 공인 IP를 가지고 있고, 인터넷에서 오는 응답을 받잖아. 그렇다면… 외부의 다른 누군가가, 우리가 요청하지도 않았는데 그냥 NAT 게이트웨이 주소로 아무 패킷이나 보내면 어떻게 되는 거야? 응답을 받기 위해 열려있는 문이라면, 원치 않는 방문객도 들어올 수 있는 거 아니야?”
3장: 외부에서 안 돼! NAT 게이트웨이의 인바운드 보호 원리
솔라는 태블릿 화면의 다이어그램을 뚫어져라 쳐다봤다. 프라이빗 서버에서 시작된 파란색 화살표는 NAT 게이트웨이를 거쳐 외부 업데이트 서버까지 갔다가, 다시 응답으로 돌아오는 완벽한 경로를 그리고 있었다. 모든 것이 깔끔하게 맞아떨어지는 흐름. 하지만 그 완벽함이 오히려 새로운 의문을 낳았다. 응답이 돌아올 수 있다면, 다른 것도 들어올 수 있지 않을까?
그녀는 펜 색상을 빨간색으로 바꿨다. 그리고 다이어그램의 ‘인터넷’ 구름 속, 아무 상관없는 지점에서부터 굵은 점선을 그어 ‘NAT 게이트웨이’ 상자를 향해 꽂았다. 기존의 파란색 요청-응답 화살표와는 전혀 다른, 초대받지 않은 방문객의 경로였다. 방금 완성된 완벽한 흐름도 위에 그려진 이 붉은 화살표는 명백한 모순이자, 그녀가 품고 있는 불안의 시각적 표현이었다.
“이거야, 언니. 이게 날 불안하게 해.”
솔라가 붉은 화살표의 끝을 톡톡 건드리며 말했다.
“업데이트 서버가 보내는 응답 패킷을 받으려면, NAT 게이트웨이는 어쨌든 외부에서 오는 트래픽을 받아들여야 하잖아. 그렇다면 이 붉은 화살표처럼, 우리가 요청한 적도 없는 악의적인 공격자가 NAT 게이트웨이 주소로 공격 패킷을 보내면 어떻게 돼? 응답이랑 공격을 어떻게 구분하는데?”
솔라의 걱정은 타당했다. 외부와 연결된 공인 IP 주소는 늘 공격의 표적이 될 수 있었다. 밖으로 나가는 길을 내준 것이 안으로 들어오는 침입 경로가 될 수 있다는 우려는 지극히 합리적인 의심이었다.
루나는 소파에 좀 더 깊숙이 몸을 묻으며, 솔라의 태블릿 화면을 차분히 들여다봤다. 그녀는 붉은 화살표를 피하지 않고 정면으로 가리켰다.
“그 걱정, 한번 끝까지 따라가 보자. 그 붉은 화살표가 NAT 게이트웨이에 도착했다고 상상해 봐. NAT 게이트웨이는 그 패킷을 받고 가장 먼저 무엇을 할까?”
“음… 자기가 가진 공인 IP로 온 거니까 일단 받겠지. 그리고… 어, 어디로 보내야 할지 결정해야 하잖아. 프라이빗 서브넷 안의 수많은 서버 중에 누구한테 온 건지 모르니까.”
“바로 그 지점이야. ‘누구한테 온 건지 모른다’는 것.”
루나는 화면을 확대해 이전 장에서 그렸던 NAT 게이트웨이의 ‘주소 변환 테이블’을 다시 보여주었다. 테이블에는 이런 기록이 남아있었다.
| 출발지 (내부) | 변환된 출발지 (외부) | 목적지 (외부) | 상태 |
|---|---|---|---|
10.0.1.5 | 52.78.100.200 | 203.0.113.10 | 응답 대기 중 |
“우리의 프라이빗 서버(10.0.1.5)가 업데이트 서버(203.0.113.10)에게 패치를 요청했을 때, NAT 게이트웨이는 단순히 주소만 바꿔준 게 아니야. 이 통신 전체를 하나의 ‘연결’로 기억하고, 그 상태를 추적하기 시작했어. 이 테이블이 바로 그 증거야.”
솔라의 시선이 ‘상태’라고 적힌 마지막 열에 머물렀다.
“자, 이제 두 가지 경우를 비교해 보자. 첫째, 업데이트 서버가 보내는 정상적인 ‘응답’ 패킷이 도착했어. 출발지는 203.0.113.10이겠지. NAT 게이트웨이는 이 테이블을 보고 어떻게 판단할까?”
솔라는 잠시 생각하더니 답했다.
“아, 테이블에 있는 목적지 주소랑 일치하네! ‘이건 10.0.1.5 서버가 시작했던 요청에 대한 응답이구나’ 하고 알아챌 수 있어. 그래서 패킷의 목적지를 10.0.1.5로 다시 바꿔서 안으로 들여보내 주겠지.”
“정확해. 그럼 두 번째 경우. 솔라 네가 그린 저 붉은 화살표, 즉 전혀 상관없는 외부 공격자(150.10.20.30이라고 하자)가 보낸 패킷이 도착했어. NAT 게이트웨이는 똑같이 테이블을 들여다봐. 어떤 판단을 내릴까?”
루나의 질문에 솔라의 눈이 반짝였다. 막혀 있던 생각의 흐름이 뚫리는 순간이었다.
“테이블에… 없어! 150.10.20.30에서 온 연결 기록은 어디에도 없어. 우리 내부 서버 중 누구도 저 주소와 대화를 시작한 적이 없으니까.”
“그럼 그 패킷은 어떻게 될까?”
“……버려지겠네.”
솔라는 나지막이 말했다. 그 한마디에 모든 의문이 담겨 있었다.
“맞아. 그냥 버려져. NAT 게이트웨이에게 그 패킷은 예약 명단에 없는 불청객일 뿐이야. 누구를 위해 온 것인지, 어떤 대화에 속한 것인지 전혀 알 수 없기 때문에 안으로 들여보내 줄 이유도, 방법도 없어. 이게 바로 ‘상태 기반’ 연결 추적의 핵심이야.”
NAT 게이트웨이는 단순한 주소 변환기가 아니었다. 그것은 내부에서 시작된 모든 아웃바운드 연결을 기억하고 상태를 관리하는 똑똑한 문지기였다. 문지기는 오직 자기가 내보낸 심부름꾼이 가지고 오는 ‘응답’만을 기다렸다가 들여보내 줄 뿐, 요청을 시작한 주체가 불분명한 낯선 방문객에게는 문을 열어주지 않았다.
“아…! 그래서 안전한 거구나. 밖으로 나가는 통로를 열어주는 게, 곧장 안으로 들어오는 통로를 열어주는 것과 같다고 생각했는데 전혀 아니었어. 요청을 시작한 주체가 누구인지, 그리고 그 요청에 대한 합당한 응답인지 확인하는 절차가 있었던 거야.”
솔라는 자신이 처음 이 주제를 접했을 때 읽었던 문장을 떠올렸다.
‘NAT 게이트웨이는 프라이빗 서브넷의 인스턴스가 인터넷에 접근할 때 내부 IP 주소를 공인 IP로 변환해주는 역할을 한다.’
이제야 그 문장의 무게가 다르게 느껴졌다. 핵심은 ‘인스턴스가 접근할 때’라는 조건이었다. 모든 것은 내부 인스턴스의 능동적인 요청에서 시작된다. NAT 게이트웨이는 그 요청에 대한 후속 처리만 할 뿐, 외부에서 임의로 시작된 연결에는 반응하지 않는다.
확신에 찬 솔라는 태블릿을 들어 펜으로 아까 그렸던 붉은 점선 화살표 위에 커다란 ‘X’ 표시를 그었다. 위협적으로 보였던 침입 경로는 이제 효력을 잃었다. 그리고 그녀는 NAT 게이트웨이 상자 옆에 작은 메모를 추가했다.
상태 기반: 내부에서 시작한 연결의 '응답'만 허용. 외부에서 시작된 요청은 폐기.
이제 솔라의 다이어그램은 완성되었다. 외부로 안전하게 나가는 길과, 외부로부터 굳게 닫힌 벽. ‘간접적으로’라는 단어 속에 숨어 있던 이 정교한 메커니즘을, 그녀는 마침내 자신의 것으로 만들었다.