개발놀이터
우당탕 홈서버 구축하기 회고 (cafe24에서 OAuth Proxy까지) 본문
이번 포스팅에선 cafe24를 거쳐 공유기 포트포워딩으로 홈서버를 구축, 그 이후에 cloudflare를 결합한 OAuth Proxy까지의 여정을 회고식으로 다뤄보려합니다.
호스팅..? 그게 뭐죠 먹는건가요
때는 2021년 6월... 벌써 4년전이네요. 개발공부란걸 처음 시작하게 되었습니다. 처음엔 jsp를 이용해서 배포를 했는데 그때 당시를 회상해보면 데이터베이스 커넥션을 맺을 때 name과 password를 어떻게 설정해줘야 하는지도 모르는 새싹이었습니다.
이때 배포라는 단어도 처음 들어보고 꾸역꾸역 만들어서 배포까지 성공을 했습니다. 프로젝트가 간단하게 게시판 있고, 서버에 있는 이미지를 보여주는 단순한 쇼핑몰 느낌이었는데 제가 했던 사이드 프로젝트인 온라인 쇼핑몰처럼 큰 규모는 아니었고 작은 규모였습니다.
파일 업로드라는 개념을 아예 모르고 있었기 때문에 서버에 이미지를 저장해두고 그 이미지를 보여주는게 전부였기에 서버가 이미지를 들고 있어야하는 문제가 있었죠.
그래서 이미지가 추가되면 서버 용량이 부족해지는 문제가 있었습니다. 제일 싼게 200MB를 저장할 수 있었는데 이미지가 200MB를 넘어가면서 500MB로 업그레이드 시켰던 기억이 있네요.
하지만 좋았던건 WAR파일만 서버에 올리면 자동으로 배포가 되었기에 리눅스를 아예 모르던 제가 배포할 수 있는 가장 좋은 방법이었습니다.
스프링 부트를 알게되고 ver.2로 업그레이드 되다
그로부터 1년뒤 영한님 강의를 듣고 스프링 부트라는걸 알게 되었습니다. 그러면서 thymeleaf를 알게되었죠. 이걸로 파일 업로드 부분을 공부하고 있었는데 문득 이런 생각이 들었습니다.
'가만보니 내 프로젝트에 파일 업로드가 없네..'
그리고 제 프로젝트를 보니 jsp로 떡칠이 되어있는 모습에 한숨이 절로 나왔습니다. 그래서 이걸 스프링 부트로 마이그레이션 해야겠다고 생각했죠.
스프링 부트로 바꾸면서 AWS S3라는 것도 적용시켜서 서버용량을 500MB씩 쓸 필요가 없어졌습니다. 다시 200MB로 내리면서 서버 비용을 아낄 수 있었죠.
추가된 기능으로 서버를 관리하기가 매우 편해졌습니다. 기존엔 이미지를 추가해야할때마다 이미지 추가 + 이미지를 보이게 하기위한 코드 추가 + 배포 이렇게 해야 이미지가 보였는데 이젠 이미지 업로드로 배포를 하지 않아도 이미지를 추가할 수 있었습니다.
하지만 이때도 유닉스 체계에 대해서는 완전 무지했기에 여전히 cafe24를 사용했고 그게 나름 잘 통했습니다.
그런 새싹이 이제 홈서버에 도전하다
2025년 4월... 그 때의 새싹이 4년이 지나고 프로젝트를 관리하던 중에 족쇄를 발견하게 됩니다. 바로 cafe24 자체가 족쇄였죠.
'아니 rm -rf 이런 명령어도 못쓰고 vim 도 못쓴다고? lsof, nslookup, dig 이런 기본적인 네트워크 디버깅도 안된다고?'
유닉스 체계에서 서버를 이렇게 저렇게 가지고 놀다보니 어느새 유닉스 체계의 운영체제와 친해졌습니다. 점점 자유도가 떨어지는 cafe24가 답답해지기 시작했습니다.
그리고 WAR파일을 배포하는것도 도커를 쓰던가 도커를 쓸 수 없는 메모리 양이라면 nohup으로 배포하면 되는걸 너무 귀찮았죠. 그러면서 드는 생각이 '그럼 1년에 2만5천원씩 주고 이걸 쓸 이유가 있을까?'
그렇게 AWS EC2로 넘어갈까 했지만.. t3a.nano가 제일 싼데 이것마저도 한달에 8천원 가까이 나옵니다. 심지어 메모리는 500MB밖에 없어서 스프링 부트가 돌아가지도 않아요. node.js 프로젝트들이나 프록시 서버로 쓰기엔 좋지만 스프링 부트 서버로 쓰기엔 턱없이 부족했고 돈도 cafe24보다 비쌌죠.
'그냥 내 맥북에다 배포해버릴까?'
홈서버 구축
가장 먼저 든 생각은
- 스프링 부트를 도커로 띄운다
- nginx를 리버스 프록시로 세워서 스프링 부트로 서빙한다. 포트는 80 포트는 443으로 리다이렉트
- 공유기 포트포워딩으로 외부 인터넷에서 우리집 공유기에 붙을 수 있게한다. 이때 외부 포트는 80, 443 내부포트는 443으로 맥북에 있는 nginx와 외부 네트워크를 연결한다.
- 도메인의 A레코드를 우리집 공유기 IP로 바꾼다.
이런 흐름을 만들었습니다. 그리고 실행에 옮겼고 인프라만 구축하면 되었기에 빠르게 개발되었습니다. 그렇게 이틀만에 개발이 완료되었습니다. 그렇게 홈서버 구축이 끝나게 되었습니다.
홈서버를 구축할 땐 가정용 공유기는 보안상 좋지않다
이런 글을 트위터에서 봤습니다. 가정용 공유기는 보안상 안좋다고? 그건 안되는데...
그리고 이 세상엔 미친놈이 많습니다. 공유기를 외부 네트워크랑 연결해놓았더니 별별 미친놈이 벨튀를 하면서 보안이 뚫리는 곳이 없는지 확인해보는 문제가 있었습니다.
퇴근하고 집에 돌아와서 로그를 확인해보면 별별 이상한 요청들이 nginx 로그에 찍혀있고 봇이 지나간 흔적이나 누군가 보안 취약점을 찾아보고 간 흔적 이런식으로 곳곳에 흔적이 남아있었습니다.
이게 뭔가.. 우리 집이 공격받는 느낌이잖아요? 아파트 현관문을 열어놨더니 자꾸 우리집에 와서 벨누르고 도망가거나 괜시리 도어락 비밀번호 한번씩 눌러보고 도망가는 느낌이었습니다.
그래서 조금 기분이 안좋았던 터에!
cloudflare zero trust
이걸 알게되었습니다. 저도 cloudflare는 말로만 들었지 실제로 써본건 처음이었는데요. cloudflare에서 파는 주력 상품 중에 zero trust라는 것이 있습니다. zero trust는 말 그대로 모든 요청을 검수하겠다는 얘기입니다. 아무도 믿지 않겠다는거죠.
이렇게 아무도 믿지 않게 되는 과정속에는 Access Policy라는 것이 있습니다. 내 애플리케이션에 접근하기 전에 cloudflare를 통과하지 못하면 내 애플리케이션으로 접근할 수 없고 그 과정에서 접근할 때 어떤 인증 방식 (e.g Google 로그인) 으로 어떤 사용자만 통과시킬지 이런 정책들을 통과해야만 내 애플리케이션에 붙을 수 있습니다.
여기서 조금만 깊게 생각해보면 그럼 cloudflare랑 내 애플리케이션을 어떻게 연결할까? 왜냐하면 내 애플리케이션이 예를 들어서 8080포트가 열려있습니다. 거길 cloudflare랑 붙으면 cloudflare가 접근 정책을 거치지 않고 바로 8080포트로 붙으면 안되니까요.
그래서 cloudflare에선 tunnel이라는 독특한 방식으로 cloudflare와 애플리케이션을 연결합니다.
애플리케이션이 있는 서버에서 애플리케이션을 로컬에 띄웁니다. 그리고 cloudflared 라는 cli 툴을 이용해서 cloudflare와 애플리케이션간 엔드 포인트를 만들어줍니다.
이렇게 되면 내 서버는 아웃바운드로 cloudflare에 엔드포인트를 만듭니다. 이 과정에서 인바운드 규칙은 아무것도 설정하지 않았습니다. 내 서버로는 아무도 못들어오는 상황이죠.
아웃바운드로 열린 cloudflare와 서버간 비밀통로를 이용해서 cloudflare는 tunnel을 만들 때 사용하는 config.yml의 규칙을 기반으로 내가 정한 도메인으로 들어오는 모든 요청을 비밀통로를 통해 밀어넣습니다. 물론 그러기 위해서는 내 도메인의 네임서버를 cloudflare의 DNS서버로 연결해야하지만요.
여기서 밀어넣는다는 표현이 적절한 것이 cloudflare는 내 도메인으로 들어오는 모든 트래픽을 서버로 위임하는 것이 아니라 그냥 밀어넣어주는 것 뿐입니다. 어찌보면 프록시라고 볼 수도 있겠네요.
이런 그림으로 되겠지요.
모든 트래픽을 cloudflare가 대신 맞아주고요 특정 도메인으로 들어오는 요청만 tunnel을 통해서 내 애플리케이션으로 들여보내주는겁니다.
이러면 두 가지 큰 장점이 생깁니다.
- 모든 트래픽은 cloudflare가 먼저 맞는다. = DDoS에 대한 어느정도의 대책이 생긴다.
- 내 애플리케이션, 내 서버는 외부와 절대 연결될 수 없다. cloudflare를 거치지 않고서는 절대 내 서버로 접근할 수 없다.
모든 트래픽이 cloudflare가 먼저 맞기 때문에 DDoS가 어느정도 방어됩니다. 왜냐하면 cloudflare는 초당 수백만 요청을 막을 수 있는 DDoS 방지 솔루션을 가지고 있기 때문이죠.
그리고 내 서버가 외부 네트워크와 완전히 단절된다는 장점도 있습니다.
이게 저에겐 엄청나게 큰 메리트였습니다. 저의 니즈와 완벽히 일치합니다. 혹시 모를 DDoS에 대비하고, 제가 정한 사람만 들어올 수 있다는것이 정말정말 좋았습니다. 거기다 이 모든게 무료입니다. 너무 아름답지않나요?
네, 아름답지 않아요.
이게 뭔... 다 적용하고 보니까 이런 페이지가 저를 반겨줍니다.
이게... 무슨...
이 페이지를 보고 충격에 말을 잇지 못했습니다. 이런 페이지가 먼저 보인다고?
에이.. 그럼 커스텀은 되겠지?
커스텀은 안됩니다.
맨위에 있는 로고를 바꾼다거나 배경색을 바꾼다거나 이정도밖에 안됩니다. 물론 유료버전은 모르겠지만요...처음엔 그냥 쓰려고 했었죠 근데 제 프로젝트는 제 소중한 사람들이 사용하는 페이지인데 이런 페이지를 보게된다니요... 정신이 아찔해졌죠.
가만... 이거 뭔가...
구글 로그인을 하고 내가 정한 이메일만 로그인할 수 있게 앞에서 막아준다.. 이거 완전 프록시잖아? 그래서 이름하야!
OAuth Proxy를 직접 만들기로 작정했습니다.
제 목표는 다음과 같았습니다.
- 단순히 OAuth 요청만 수행한다.
- 요청이 끝나면 뒤에 있는 애플리케이션으로 요청을 서빙한다.
- 프록시로 사용할 수 있을 정도로 가벼워야한다.
그렇게 Express를 이용해서 가볍게 프록시를 만들었고 이렇게 변했습니다.
- 모든 요청은 cloudflare가 먼저 맞는다. (DDoS 방지)
- cloudflare tunnel은 OAuth Proxy와 연결되어있다.
- 모든 요청들은 /login으로 리다이렉트.
- OAuth Proxy에서 OAuth인증을 거친 후 등록된 사용자인지 프록시에서 확인한다.
- 등록된 사용자라면 뒤에 있는 실제 애플리케이션으로 서빙
- 실제 애플리케이션은 필터에서 해당 요청이 OAuth Proxy에서 서빙된 것인지 확인하고 아니라면 403을 반환 이때 Origin, Referer를 확인함.
- Origin, Referer는 얼마든지 클라이언트 쪽에서 변조가 가능하기 때문에 OAuth Proxy에서 뒤에 있는 애플리케이션에 전달할 때 특별한 HTTP Header를 사용. 저는 X-Forwarded-Proxy라는 헤더를 전달해줬습니다.
- 애플리케이션에서 Origin, Referer, Header를 모두 검사하고 통과
제가 구현한 OAuth Proxy는 cloudflare zero trust를 모방한 프록시이고 zero trust의 다양한 Access Policy들은 제 입장에서 필요없는 기능이었고 제가 필요한건 오직 "내가 등록한 사용자만 입장할 수 있게 한다." 였기에 직접 프록시를 만드는 것이 합리적이라고 생각했습니다.
물론 Access 페이지가 너무 구려서 만든거지만요.
마치며
oauth-proxy라는 이름은 제가 만든건 아니고 실제로 oauth-proxy라는 이름의 오픈소스가 있더라구요. 아마 제가 구현한거보다 조금 더 추상화 되어있고 사용하기 쉽게 되어있을겁니다.
그럼에도 제가 직접 만든 이유는 뭔가 개발자들이 자기가 필요한걸 직접 뚝딱뚝딱 만들어서 사용하는게 너무 멋있어보였습니다. 지금은 AWS가 상업목적으로 클라우드를 만들지만 초기 의도는 자신들이 사용할 파일 저장소가 필요했던 것처럼요.
그래서 저도 개발자 감수성을 터트려가면서 직접 만들어봤습니다. 직접 만드니까 너무 뿌듯하고 저도 어엿한 개발자로 성장한거같아서 매우 뿌듯하고 좋았습니다.
이번 포스팅은 여기서 마치도록 하겠습니다. 오늘도 즐거운 하루 되세요~
'기타 > 회고' 카테고리의 다른 글
취업한지 만으로 1년을 되돌아보다 (0) | 2025.02.28 |
---|---|
회고다운 회고.. (0) | 2024.11.19 |
개발 공부한지 3년을 채우고 다시한번 뒤를 돌아보다 (0) | 2024.07.10 |
신입 개발자입니다. (0) | 2024.01.30 |
4년간 대학교를 다니며... 대학생활 회고 (0) | 2024.01.18 |