목표
도메인을 구매하고, 서브도메인을 활용해 다양한 서버(ip)를 하나의 도메인에서 연결할 수 있도록 붙여보자! (2021년 8월 14일 기준으로 작성된 글입니다!)
들어가기에 앞서
백엔드 서버는 [리버스 프록시 서버(nginx)] - [백엔드 서버(spring boot)]로 구성되어 있습니다.
인프라로는 AWS의 EC2 (ubuntu 18.04ver) 2 대를 활용하고 있습니다.
프론트 서버는 [CDN 서버] - [정적 파일 서버(리액트 빌드 결과물 업로드)]로 구성되어 있습니다.
CDN 서버는 AWS cloudfront, 정적 파일서버는 AWS S3를 활용하고 있습니다.
도메인 구성
도메인도 돈이니까, 하나의 도메인만 구매 후 서브 도메인을 활용해 여러가지 서버의 자원을 보여줄 수 있도록 구성하면 어떨까? 생각한 것이 시작이었습니다. 먼저 다음과 같은 꿈을 꾸어보았습니다.
: example.com을 구매하면 다음처럼 도메인을 구성할 수 있지 않을까?!
example.com : 프론트엔드 운영 서버 (위와 같음)
dev.example.com : 프론트엔드 개발 서버
api.example.com : 백엔드 API 서버(스프링이 돌아가고 있는 서버, 내 경우 Nginx서버)
꿈을 이루기 위해 한걸음씩 해보겠습니다.
도메인 구매
도메인을 판매 및 DNS 관리 서비스를 제공하는 국내 호스팅업체로는 CAFE24, 가비아, 후이즈, 호스팅KR 등이 있습니다. 이 중 저는 가비아나 후이즈를 추천하는데요, 그 이유로는 카페24와 호스팅KR에선 CNAME을 통한 루트 도메인 등록이 UI를 통해선 불가능하기 때문입니다ㅎ (21년 8월 14일 기준) 고객센터에 요청하면 업체에서 등록해주는 방식입니다. CNAME에 관해선 아래에서 설명하겠습니다.
여담으로 저는 프로젝트에서 활용할 도메인을 호스팅 KR에서 구매했었는데요.. 고객센터까지 연락하는 과정이 조금은 번거롭게 느껴졌답니다..^^
그 와중에 루트 도네임이라고 오타 냈었내요..^^
이번 포스팅에선 가비아를 통해 도메인 구매 및 IP 연동을 진행해보도록 하겠습니다.
가비아에서 도메인 구매하기
1.
가비아에 회원가입 (저는 카카오 소셜로그인으로 진행했습니다)
2.
가비아에서 원하는 도메인 검색
원하는 도메인을 선택하여 구매하시면 됩니다. 가격은 1년 기준이므로, 이쁜 이름과 가격간의 타협을 보시길 바랍니다 ^_^ 참고로 이벤트 가격은 첫 1년만 적용됩니다.
이번 포스팅에선 wootecotest.shop이라는 도메인을 구매하여 진행하겠습니다! (500원이라는 거금 투여) 이후 포스팅은 해당 도메인을 예시로 이루어집니다.
3.
가비아 결제
원하는 도메인을 선택하셨다면 결제를 진행하시면 되는데요, 만약 이벤트 가격이 진행중인 상품을 선택하셨다면 등록기간이 3년이 디폴트로 잡혀있는 것만 주의하시면 되겠습니다.
(1년으로 바꾸어주셔야 이벤트 가격이 적용됩니다)
관리자 정보 같은 경우 도메인서버에 등록되는 정보이기 때문에 개인정보가 노출되는 걸 원치 않는다면 소요자 정보와 같음 체크박스를 해제하고 회사 정보로 적어주셔요! 저희팀 같은 경우 제 개인정보로 등록했기 때문에 jujeol-jujeol.com 도메인을 조회해보면 제 집주소 전화번호가 다 나온답니다 ^_^..!
이후 나오는 결제절차는 기존 쇼핑몰 등과 유사하기 때문에 생략하겠습니다.
업체에서 등록하는 시간이 있기 때문에, 결제한다고 도메인이 바로 등록되지 않습니다!!! 4번 정도 구매해 본 체감상 결제 후 10~15분 사이에 등록이 되었던것 같습니다. 가비아 같은 경우 등록이 완료되면 문자로 통지해줍니다.
4.
홈 > 마이가비아로 이동
a.
등록이 정상적으로 진행되었다면 도메인이 제공중인 것을 확인할 수 있습니다. DNS 관리툴을 눌러줍니다.
5.
내 도메인 목록을 확인할 수 있고, 필요하다면 설정 버튼을 눌러 도메인 관련 정보를 등록할 수 있습니다.
CNAME 레코드로 cloudfront 프론트서버 연결하기
DNS 레코드
레코드 수정을 들어가보면 다음과 같은 레코드들을 등록할 수 있는 것을 볼 수 있습니다.
이 중 저희가 다룰 것은 A레코드와 CNAME 레코드입니다.
A레코드와 CNAME레코드에 대해 알아봅시다!
A레코드
도메인의 정의를 생각해보시면, A레코드에 대해선 이해하기 쉽습니다. 정수로 이루어진 ip를 문자로 된 도메인 이름으로 연결해주는 DNS의 정의에 가장 충실한 것이 A레코드입니다.
위와 같이 등록하면, http://wootecotest.shop으로 접속했을 때 123.123.123.123 ip로 연결해준다는 의미입니다. {@ 호스트는 가비아에서 루트 도메인(www. 같은 서브도메인이 안 붙은 도메인)을 의미하는 기호입니다. 루트 도메인을 의미하는 기호는 호스팅 업체마다 다릅니다.}
CNAME 레코드
CNAME 레코드는 canonical name의 약자로, ip가 아닌 다른 도메인 이름으로 된 주소로 연결할 때 활용할 수 있는 레코드입니다.
위와 같이 등록하면, http://wootecotest.shop 로 접속했을 때 http://d1uwbfuq9d0yvy.cloudfront.net 서버의 자원으로 연결하게 됩니다. 리다이렉트와 달리 클라이언트의 도메인은 변경되지 않고 그대로 유지됩니다. 일종의 alias라고 생각할 수 도 있겠네요! CNAME은 규약에 의해 도메인 값 끝에 . 이 붙어야하지만, 호스팅 업체에 따라 입력하라는 곳도 하지 말라는 곳도 있습니다.
CNAME 레코드 등록하기
d1uwbfuq9d0yvy.cloudfront.net 라는 도메인 주소는 제 프론트 서버의 CDN url이고, AWS에서 제공해주고 있는 도메인 주소이기 때문에 제가 실제 IP를 알 수 없습니다.
그러므로 저는 A레코드로 ip를 직접 등록할 수 없고 CNAME으로 등록해야 합니다.
제 목표는 www.wootecotest.shop 과 wootecotest.shop 으로 접속했을 때 는 d1uwbfuq9d0yvy.cloudfront.net에 들어왔을 때와 동일한 화면이 보여지는 것이므로, 레코드를 등록하고 진행해보겠습니다! ㅎ_ㅎ
도메인 레코드를 수정하고 적용되는 시간은 체감상 10초 이내로 이루어지는 것 같습니다.
자! 도메인 등록 끝! 부푼 꿈을 안고 도메인에 접속해보면!
잉ㅠ 403ㅠ 넌 못 지나간다 엔딩인데요, 이는 AWS 클라우드프론트에선 허가되지 않은 CNAME 도메인에서의 접속을 차단하고 있기 때문입니다. 아마존님의 허락을 얻으러 떠나봅시다.
클라우드 프론트에서 Alternate CNAME 등록하기
1.
AWS 클라우드 프론트 웹콘솔로 이동하여 프론트 서버를 연동하고 있는 Distribution을 클릭해 봅시다.
2.
상세화면에서 edit를 클릭합니다
3.
Alternate domain name (CNAME)에 구매한 도메인을 추가합니다.
인증서 등록하기
ACM(AWS Certificate Manager)에 도메인 인증서를 등록하기!
1.
수정화면에서 CNAME 등록칸 아래쪽에 SSL 인증서 등록칸이 있습니다. 신규 인증서 등록을 해봅시다.
2.
저는 서브도메인 전체와 루트 도메인에 대해 인증서를 발급받기 위해, 와일드카드 도메인와 루트도메인 두 가지를 추가해주겠습니다.
왜 여러 도메인을 등록하죠? → 와일드카드 인증서 (*)
ssl인증서는 도메인 하나하나에 대해 발급됩니다. wootecotest.shop 에 대한 인증서를 등록해도, www.wootecotest.shop 에 대해선 인증서가 없는 것으로 취급되기 때문에 https 프로토콜을 활용할 수 없습니다. 서브 도메인에 대해선 따로 인증서를 등록해줘야 하는데, www 같은 서브도메인에 대해서만 인증서를 등록할 수 도 있고, 와일드카드(*)를 통해 모든 서브도메인에 대한 인증서를 등록할 수 도 있습니다.
위의 예시에서 *.wootecotest.shop에 인증서를 등록하고나면, 저는 www.wootecotest.shop 과 dev.wootecotest.shop 전체에 대해 https 프로토콜을 활용할 수 있게 됩니다.
3.
검증 방법은 DNS 검증을 활용하겠습니다
4.
태그 추가는 크게 중요하지 않지만 웹 콘솔에서 쉽게 식별하기 위해 Name정도는 주겠습니다! 안 줘도 무방합니다.
5.
이제 위에 선택한 DNS 검증을 수행할 때가 되었습니다.
DNS 검증이란?
SSL 디지털 인증서를 활용하는(https 프로토콜을 활용하는) 이유는 2가지 입니다.
•
http 통신 내용을 도청하거나 위조하는 것을 막기 위해.
•
클라이언트가 접속하려는 서버가 신뢰 할 수 있는 서버인지를 판단할 수 있게 하기 위해.
이 중 클라이언트가 접속하려는 서버가 신뢰 할 수 있는지 보장하는 역할을 하는 것이 CA(Certificate authority)입니다. CA들은 민간기업으로, 공인된 CA의 목록은 브라우저에 내장되어 있습니다. ACM에 등록하는 절차 또한 CA에 인증서를 등록하는 절차입니다.
CA는 클라이언트가 접속하려는 서버가 신뢰 할 수 있는지 보장하는 역할 을 다하기 위해, 신청자에게 신청한 도메인이 본인이 소유한 도메인임을 입증할 것을 요구합니다.
DNS 검증은 인증 방식 중 하나로, CA가 요구하는 특정한 문자열을 내 도메인에 등록하는 것을 통해 검증하는 것입니다. 저는 핸드폰 본인인증 방식같다고 생각했는데요, 핸드폰 본인인증에선 아무 숫자 6개를 보내주고 확인해서 콘솔에 입력하라고 하죠? 거의 똑같은 과정입니다.
ACM은 CNAME 레코드로 _d7bb.....를 호스트로 가진 서브도메인을 만들어, _2562c180... 문자열을 값으로 입력해보라고 요구하네요. 제가 해당 도메인을 가지고 있지 않다면 할 수 없는 활동입니다.
시키는 대로 해봅시다!!
6.
가비아의 도메인 레코드 수정으로 돌아와서, 위에서 요청한대로 CNAME 레코드를 생성해줍니다.
체감상 저장 후 30초 이내에 검증 보류 → 발급 완료 상태로 전환이 됩니다. 아무리 기다려도 전환되지 않는다면 잘못 입력한 게 없는지 확인해봅시당!
7.
이제 해당 인증서를 사용할 수 있습니다. CloudFront → distrubutuion → edit → CNAME을 등록하는 화면으로 돌아갑시다!
등록하려는 도메인에 걸맞은 인증서를 등록했다면, 이제 저장할 수 있습니다.
(참고로 하나의 cloutfront distribution에는 하나의 인증서만 적용할 수 있습니다.)
8.
이제 다시 한 번 wootecotest.shop에 접속해보겠습니다. 인증서도 적용했기 때문에, 443포트로 접속해(https 프로토콜) 보겠습니다
어..? 뭔가 또 안 되는 거 같은데요..?
이건 제가 게을러서 예제 S3 버킷에 index.html을 안 올렸기 때문입니다 ㅎ 없는 자원을 요청했을 때 에러페이지 처리도 안 해줬습니다 왜요 뭐요
농담이에요 화 안났어요 사랑해요 여러분~~
요점은 도메인에 CNAME으로 연결한 서버와 동일한 내용이 뜨기만 하면 되는거니까.. wootecoteset.shop과 d1~~~.cloudfront.net의 요청이 똑같이 뜨므로 잘 동작했네요! 도메인 붙이기 성공! ㅎ_ㅎ
9.
DNS 검증을 위해 등록했던 CNAME 레코드는 삭제해도 됩니다. 현재까지 진행상황은 다음과 같네요. 루트도메인과 www 서브도메인에 대해 프론트엔드 서버로의 연결이 완료되었습니다.
목표 중간 점검
제 목표를 중간점검 해볼까요?
example.com : 프론트엔드 운영 서버 (위와 같음)
dev.example.com : 프론트엔드 개발 서버
api.example.com : 백엔드 API 서버(스프링이 돌아가고 있는 서버, 내 경우 Nginx서버)
•
도메인 구매 → CNAME 등록 → CloudFront에 Alternate CNAME 등록 → ACM 공인인증서 등록 → DNS 검증 → CNAME 최종 등록 순서로 CNAME을 등록해보았습니다. dev는 CNAME을 통해 연결해주는 CloudFront Distribution url만 다를 뿐 동일한 과정이므로 생략할께요. 다음처럼 등록하면 되겠죠?
다음은 A레코드로 백엔드 EC2 서버 연결하는 법을 알아보겠습니다.
A레코드로 백엔드 서버 연결하기
백엔드 서버 A 레코드 연동은 단순합니다. 연동하고자 하는 NGINX가 띄워져 있는 서버의 퍼블릭 ip를 넣어주면 됩니다.
하지만 문제가 있었는데요, https 연결을 하기 위해선 nginx가 ssl 개인키와 공인인증기관의 인증서를 가지고 있어야 ssl 통신이 가능합니다. ACM에서 등록한 인증서가 있으므로, 해당 인증서를 활용할 수 있지 않을까? 생각했습니다.
그러나 이럴수가.. ACM은 개인키와 인증서를 다운로드 할 수 있게 해주지 않습니다.
ELB 또는 cloudfront를 이용하라네요. 저희 프로젝트에선 AWS 기술스택에 대한 제한이 있기 때문에 그 방법을 활용할 수 없었구요. 대체제로서 api서버 인증서는 certbot과 무료 인증서로 핫한 letsencrypt를 활용하기로 했습니다.
도커를 통한 certbot 컨테이너를 활용하여 인증서를 등록했습니다. letsencrypt 와 nginx 설정에 관한 레퍼런스는 워낙 많기 때문에 생략하겠습니다!
해당 과정에서 생겼던 궁금증과 주의점만 적어보겠습니다.
certbot 와일드카드 인증서 등록
1.
nginx 연결
$ docker run -it --rm --name certbot \
-v '/etc/letsencrypt:/etc/letsencrypt' \
-v '/var/lib/letsencrypt:/var/lib/letsencrypt' \
certbot/certbot certonly -d '[우리 도메인 이름]' -d '*.[우리 도메인 이름]' --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
Java
복사
lets encrypt는 dns 검증을 활용하면 와일드카드 도메인을 등록할 수 있습니다.
마지막 줄을 주목해 주시면, certonly -d '[우리 도메인 이름]' -d '*.[우리 도메인 이름]'
에 -d를 두번 이용하여 도메인 이름과 *.도메인 이름 두 가지를 등록하는 것을 볼 수 있습니다.
왜 여러 도메인을 등록하죠?
제 목적은 api.wootechtest.shop 에 적용하려는 것이므로 해당 도메인만 인증서를 적용해도 상관없습니다만, 이후 어떻게 확장될 지 몰라 와일드카드로 등록하였습니다.
letsencrypt도 도메인 인증을 요구하는데, 가비아에서는 다음과 같이 처리하면 됩니다.
# letsencrypt에서 요구하는 DNS 검증
---
Please deploy a DNS TXT record under the name
\_acme-challenge.wootecotest.shop with the following value:
tRVvPUXMXgVVTU2Kmi0qQ2kLyfzxhvdaQzNcp9cWoRg
Before continuing, verify the record is deployed.
Shell
복사
이미 사용하고 있는 nginx의 인증서 변경하기
(기존에 다른 도메인 인증서로 구동하고 있는 nginx가 있다는 전제하에)
위의 DNS 검증을 마치고 생성된 pem키를 nginx로 전달해주어야 합니다.
# 발급한 인증서로 교체하기
$ cp /etc/letsencrypt/live/[도메인주소]/fullchain.pem ./내_nginx.conf에서_정의한_fullchain.pem_경로
$ cp /etc/letsencrypt/live/[도메인주소]/privkey.pem ./내_nginx.conf에서_정의한_privkey.pem_경로
Shell
복사
만약 nginx를 docker 컨테이너 환경에서 구동하고 계신 분이라면 docker cp 키워드에 대해 학습해보시면 pem 파일을 교체해주시면 되겠네요!
만약 기존 도메인에서 구매한 도메인으로 nginx의 인증서를 변동하시려는 분들은, 해당 파일을 nginx.conf에서 정의한 경로로 옮겨주고 혹시 모르니 nginx도 reload해주시면 되겠습니다.
# nginx.conf
events {}
http {
upstream app {
server 172.17.0.1:8080;
}
# Redirect all traffic to HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
# 새로 발급한 인증서를 옮겨주어야 할 경로!!!
ssl_certificate /etc/letsencrypt/live/[도메인주소]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[도메인주소]/privkey.pem;
# Disable SSL
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 통신과정에서 사용할 암호화 알고리즘
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
# Enable HSTS
# client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제합니다.
# 이를 통해 http에서 https로 redirect 되는 request를 minimize 할 수 있습니다.
add_header Strict-Transport-Security "max-age=31536000" always;
# SSL sessions
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://app;
}
}
}
Java
복사
# restart는 재시작, reload는 재시작없이 바뀐 설정파일만 적용
service nginx reload
Shell
복사
클라이언트는 두개의 인증기관으로부터 인증받은 도메인은 어떻게 처리할까?
여기까지 작업을 해놓고 나니 클라이언트(브라우저) 입장에서는 https를 통해 서브도메인으로 접근할 때, Amazon과 lets encrypt 두 가지 CA로부터 인증서를 발급받은 도메인인데, 어떻게 둘 중 어떤 인증서를 활용 해야하는지 정확히 찾아서 하는지 궁금증이 생겼습니다.
실수로 다른 공인인증기관의 인증서를 가져와서 서버에 요청하면, 공개키와 비밀키가 서로 맞지 않으므로 오류를 일으킬겁니다.
그러나 두개의 와일드카드 인증서를 가지고 있는 api.wootecotest.shop와 www.wootecotest.shop에 수십 수백번 요청해봐도 api.wootecotest.shop은 항상 lets encrypt의 인증서를, www.wootecotest.shop은 amazon의 인증서를 가져옴을 확인할 수 있었습니다.
인증서의 상세 정보는 크롬브라우저에서 자물쇠 → 인증서를 눌러 확인할 수 있습니다
어떤 인증서를 활용할지 식별하는 부분은 https에 관해 더 학습하면서 알 수 있었습니다.
https 프로토콜은 우선 TCP 3way-hand shaking을 하는 부분은 http와 동일합니다. 이후 TLS 암호화를 위해 SSL Handshake를 시작합니다. 이 과정 중 서버는 Server Hello과정에서 서버의 공개키가 담긴 SSL 인증서(fullchain.pem)를 클라이언트에게 전달하는데 이 인증서에는 공인인증기관 정보가 함께 담겨있기 때문에, 브라우저는 헷갈리지 않고 서버에서 알려준 CA에게 인증서를 요청할 수 있었던 것 입니다.
장황한 긴 글 봐주셔서 감사합니다~~! 누군가에게 도움이 되길 바라며..