2021.09.15 - [Course/django : 제로부터 시작하는 인스타그램] - Django:제로부터 시작하는 인스타그램 만들기 - clone instagram 목차
유튜브 버전 - https://youtu.be/DKDod2Nl_Ww
시작하기 전에
❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗
이 강의는 ROUTE53을 이용해 도메인을 발급받아야 SSL인증서를 발급받을 수 있습니다. AWS에서 ROUTE53을 이용한 도메인 발급 비용은 보통 1년에 만원정도 합니다. 발급하지 못하면 SSL적용이 힘드니 참고해 주세요~
❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗
SSL(Secure Sockets Layer)이라는 말을 많이 들어보았을 겁니다. 약간 연차가 있는 분들은 TLS라고 부르기도 하는데 같은 말입니다. ㅎㅎ
HTTP통신에 암호화를 추가해 보안성을 높여 HTTPS 통신으로 만들어주는 게 바로 SSL입니다. 아마 여러분들 웹 서핑을 할 때 어떤 페이지는 HTTP고 어떤 페이지는 HTTPS였던 걸 기억하실 겁니다. 큰 웹사이트는 대부분 HTTPS지만, 개인 웹사이트나 작은 웹은 아직도 HTTP를 사용하는 곳이 많습니다. 요즘엔 브라우저가 HTTP 웹사이트에 들어가면 경고를 띄워주더라고요.
HTTPS가 HTTP와 다른 점은 간단합니다. 서로 통신을 할 때 암호화를 하냐 안 하냐 차이입니다.
ROUTE53 도메인 발급하기
SSL을 적요하려면 도메인이 필요하기 때문에 우선 도메인 발급부터 진행하겠습니다. AWS에서 ROUTE53이라는 서비스를 이용해 도메인을 발급할 수 있습니다. (유료!!😣)
ROUTE53에서 왼쪽에 등록된 도메인을 누르면 도메인을 등록할 수 있습니다. 도메인 등록 버튼을 누르면 도메인 이름을 정할 수 있습니다. 이미 있는 도메인은 안됩니다 ㅎㅎ 이미 있으면 다른 도메인을 추천해 줍니다.
가격이 아주 비싸졌네요,, 작년엔 5달러짜리도 있었는데 ㅠㅠ
가장 저렵한 도메인을 선택하고 장바구니에 담으면 결제화면으로 넘어갑니다. 결제화면에서 개인정보들을 입력하면 구매 완료!!
시간이 어느 정도 지나면 구매가 확정되고 도메인을 사용할 수 있게 됩니다.
저는 이미 nullnullstudio.co라는 도메인을 사용하고 있기 때문에 해당 도메인으로 포스팅을 진행하도록 하겠습니다.
HTTPS의 상세 과정
HTTPS의 과정을 다음과 같이 진행됩니다.
- 클라이언트와 서버 간에 랜덤 값(서버와 클라이언트 각각 생성) 교환 (이때 서버에서는 인증서도 함께 전송)
- 클라이언트에서는 수신된 인증서 검증 (인증 기관을 통해서 검증)
- 클라이언트는 서버와 클라이언트가 생성한 랜덤값을 섞어서 pre-master secret을 생성 후, 인증서의 공개키(인증서에 포함되어 있음)로 암호화하여 서버로 전송
- 서버는 인증서의 개인키(서버만 가지고 있음)를 사용해 pre-master secret을 복호화합니다.
- 서버와 클라이언트 각각 정해진 암호화 방식을 사용하여 pre-master secret을 암호화하고, 해시 함수를 통해 해싱합니다. 이때 나온 결과값이 master secret입니다.
- 이제 서버와 클라이언트 모두 동일한 master secret 값을 가지고 있으므로, 이 값을 이용해 통신할 때 전송하는 데이터를 암호화/복호화합니다.
글로 열심히 적어보았는데, 풀어서 설명을 해 볼까요? 일단 1번에서 클라이언트가 먼저 서버에 랜덤 한 값을 전송합니다. 추가로 어떤 master secret을 만들 때 클라이언트가 사용 가능한 암호화 방법들을 전달합니다. 서버는 요청을 받으면 역시 랜덤 한 값을 클라이언트에게 전달하고, 클라이언트가 전달한 암호화 방법들 중에 가장 안전한 방법을 고릅니다.
아래 그림은 클라이언트에서 "mychew"라는 랜덤 값과 ["AES", "3DES"..]로 암호화 방법들을 리스트 형태로 전달하는 모습입니다. 서버는 마찬가지로 "youtube"라는 랜덤 값을 전달했고, 암호화 방법들 중에 "AES"를 선택했습니다. 그리고 제일 중요한 "인증서"를 보냅니다.
이렇게 인증서를 전달받은 클라이언트는 해당 인증서가 신뢰할 수 있는 인증서인지 검증합니다. 해당 인증서에는 인증서 발급기관과 공개키 등의 정보가 있는데, 인증서 발급기관(CA)이 신뢰할 수 있는 발급기관인지 확인합니다. 여기서 이제 어떻게 신뢰할 수 있을까?라는 고민을 할 수 있는데, 보통 웹 브라우저나 OS는 신뢰할 수 있는 인증기관 목록을 가지고 있기 때문에 해당 인증서가 목록에 있는지 검사하게 됩니다. 만약 목록에 없는 인증서 발급기관이라면, 함께 온 공개키를 이용해 서명검증을 하게 됩니다. 이때 서명검증을 해서 인증서가 유효한지는 확인할 수 있지만, 신뢰할 수 있는 인증기관은 아니기 때문에 주의해야 합니다. 가끔 웹페이지 들어갔을 때 신뢰할 수 없는 인증서!!라고 빨갛게 뜨는 사이트가 있는데, 사설 인증서를 사용하면 요롷게 뜹니다 ㅎㅎ.
인증서확인이 끝나면 클라이언트는, 서버와 클라이언트가 각각 생성한 랜덤값을 가지고 공개키로 암호화합니다. 이 값이 pre-master secret 값입니다.
이제 클라이언트와 서버가 모두 pre-master secret값을 가지고 있습니다. 이제 앞서 정의한 암호화 방식을 통해 이 값을 암호화하고 해싱함수를 통해 해시하면 master secret값이 나오게 됩니다. 지원하는 암호화 방식이나 해시방식은 TLS 스펙에 따라 다릅니다. (보통 라이브러리가 알아서 정해주니,,,)
이렇게 양쪽에 master secret이 생겼으니, 이 값으로 이제 암호화해서 데이터를 전달하면 됩니다.
master secret은 대칭키이기 때문에 암호화 한 데이터를 복호화하기 위해서 그대로 master secret을 사용하면 됩니다.
우리 서버에 SSL 적용하기
자 이제 이론을 알았으니 적용을 해볼까요? SSL을 적용하는 방법은 여러 가지가 있는데, AWS인증서를 사용해서 AWS ALB(Application Load Balancer)를 이용해 적용하는 방법이 있고, 사설 인증서를 만든 다음 NGINX에 적용하는 선택지가 있습니다. 양쪽 다 SSL을 이용하는 건 동일한데 한 가지 고려해야 할 점이 있습니다. 바로 '어디서 암호화/복호화를 하느냐'인데요,
NGINX에 인증서를 등록해서 암/복호화를 한다면 EC2에서 암복호화가 진행됩니다. 그럼 저희 EC2의 CPU와 메모리가 소모됩니다. 하지만 AWS에 ALB에 인증서 등록하고 사용하면 암복호화를 ALB가 대신해 주게 됩니다. 그 후 ALB와 EC2사이의 통신은 HTTP로 이루어지기 때문에 EC2에서는 암복호화를 신경 쓰지 않아도 됩니다. 암복호화를 보통 SSL 터미네이션이라고 하는데, SSL 터미네이션을 어느 구간에서 하는지가 서버 성능에 영향을 미치기 때문에 대용량 서비스를 구축할 때는 신중해야 합니다. 물론 저희는 상관없지만요 😅
아 그리고 ALB에서 SSL 터미네이션을 한다고 해서 서버가 무조건 좋은 건 아닙니다. ALB도 사용 가능한 자원이 있기 때문에 많은 요청이 들어오면 힘들어합니다. AWS의 ALB는 요청에 따라 자동으로 서버가 늘어나고 줄어드는 auto scale이 되기 때문에 그나마 EC2보다는 설정이 간단합니다. (EC2도 auto scale 되지만,,, ㅎㅎ)
자 그럼 AWS에 ALB를 이용해 SSL통신을 적용해 볼까요? (생각보다 간단함 주의)
일단 ALB를 만들어야 하는데 ALB는 EC2 대시보드에서 생성할 수 있습니다.
생성하기에 들어가면 ALB, NLB, GLB 등을 선택하는 화면이 나옵니다. 저희는 ALB를 생성합니다.
그럼 이제 상세 설정화면이 나오는데요, 일단 Load Balancer name을 정합니다. 저희는 django_zero_alb라고 할게요.
그리고 아래 VPC는 아마 디폴트로 하나가 되어있을 겁니다. 그냥 건들지 않으면 되고, 아래 mappings에서 AZ 두 개를 골라야 합니다. 이전에 말했다시피 고가용성을 지원하기 위해 AZ는 2개 이상으로 골라야 합니다. 여기서는 우리의 EC2가 어디에 위치에 있는지 확인하고 골라야 합니다. 제 EC2는 a존에 있기 때문에 a와 c를 고르겠습니다.
그 아래 보안그룹(security group)은 디폴트로 설정하고, 제일 중요한 Listeners and routing 박스를 봅시다. 여기에서 프로토콜을 선택할 수 있는데, 우리는 ssl을 사용하기 때문에 https를 선택해 줍니다. 그럼 포트번호가 443으로 변경됩니다. 옆에는 타겟 그룹(target group)을 정하라는 창이 있는데 타겟 그룹이란 ALB가 바라보는 ec2 그룹입니다.
이렇게 타겟 그룹을 정하게 되면 ALB는 타겟 그룹안에 있는 EC2에 들어온 요청을 골고루 분배하게됩니다. 하지만 저희는 EC2가 한대니까 ALB로 들어온 요청은 모두 하나의 EC2로 가게되겠죠? ㅎㅎ 그래서 사실 EC2가 하나면 필요가 없긴한데,, SSL를 적용하기 위해서 ALB를 사용하는 겁니다. 참고로 이런 ALB의 로드밸런싱 기능은 NGINX에서도 가능합니다.
따라서 우리는 타겟 그룹을 만들어주고, 그 안에 우리가 만든 EC2하나만 넣어주면 됩니다. create target group을 선택하면 타겟 그룹을 어떻게 만들래?라는 화면이 뜨는데 instances를 선택하고 아래 Target group name은 아무렇게나 지어줍니다.
여기서 중요한 것은 사용하는 프로토콜이 HTTP라는 점입니다. 클라이언트와 ALB사이는 HTTPS지만 ALB에서 SSL 터미네이션을 하기 대문에 ALB와 EC2사이는 HTTP통신입니다.
아래로 내리면 Health checks가 있는데, 이것은 ALB가 EC2에 헬스체크를 할 수 있도록 헬스체크용 API url을 입력하라고 나옵니다. 저희는 헬스체크를 안 만들었는데 말이죠.. ㅎㅎ 일단 /admin이라고 아무거나 get으로 요청했을 때 200 응답이 내려오는 url을 입력해 줍니다.
타겟 그룹을 생성하고 나면 타겟 등록(Register targets)화면이 나옵니다. 여기서 EC2들 중에 만드려는 타겟 그룹에 들어가는 EC2를 선택할 수 있습니다. 저희가 만든 인스타 EC2를 선택합니다.
Include as pending below 버튼을 누르면 선택한 ec2가 아래 Review targets로 내려갑니다. 이제 해당 EC2는 타겟 그룹 소속입니다. ㅎㅎ create target group을 눌러서 생성을 마무리합니다.
그리고 다시 이전 ALB생성으로 돌아가서 우리가 방금 만든 타겟 그룹을 지정해 줍니다.
옆에 새로고침 버튼을 누르면 타겟 그룹 목록이 갱신되기 때문에 나오지 않는다면 한번 눌러줍니다.
이제 바로 아래 Secure listener settings가 있는데 여기가 바로 대망의 SSL설정입니다. Security polict는 사용하는 TLS 버전인데, recommended가 디폴트로 설정되어 있기 때문에 건드리지 않습니다. 오른쪽에는 SSL/TLS certificate가 있는데 여기에 바로 인증서를 넣어야 합니다. 하지만 저희는 인증서가 없잖아요? AWS에서 인증서도 발급해 줍니다. 😃
AWS에 인증서 서비스는 바로 ACM입니다. AWS Certificate Manager의 약자인데요, 아래 파란색으로 쓰여있는 Request new ACM certificate를 누르면 인증서를 발급할 수 있습니다.
먼저 도메인 이름을 적어야 하는데, 앞서 생성했던 route53의 도메인을 이용합니다.
메인 도메인이 nullnullstudio.co이고 앞에 서브 도메인을 django-devops라고 설정했습니다.
새로고침을 하면 새로운 인증서가 생성되었는데 아직 부적격입니다. DNS를 이용한 검증을 해야 하기 때문입니다. 인증서 ID를 눌러서 들어가면 상세 정보가 나오는데 여기서 Route 53에서 레코드 생성을 누릅니다.
그럼 자동으로 인증서에 해당하는 DNS레코드를 생성해 줍니다. 레코드 생성을 누릅니다.
그럼 route53에서 호스팅 영역에 레코드가 하나 생성된 게 확인됩니다. (아직 django-devops.nullnullstudio.co라는 서브도메인 호스팅은 생성하지 않았습니다.)
자 이제 인증서를 만들었으니 다시 ALB생성 화면으로 돌아갑니다.
자 이제 ALB를 생성할 수 있습니다.
이제 마지막으로 route53에서 django-devops.nullnullstudio.co라는 호스팅을 만들고 방금 만든 alb랑 연동해 줍니다.
레코드 생성을 누르면 라우팅 정책이 나오는데 단순 라우팅을 선택하고 다음을 누릅니다. 그다음 단순 레코드 정의를 누르고 subdomain인 django-zero를 입력해 줍니다. 레코드 유형은 A이고 엔드포인트는 ALB를 선택합니다.
레코드 생성을 눌러서 레코드를 생성합니다.
자 그럼 한번 접속해 볼까요?
https://django-zero.nullnullstudio.co/main으로 가봅시다
크흑 역시 한 번에 되지 않는군요. 아마 방화벽 문제인 것 같아서 보안그룹을 한번 봐줍니다. 보안그룹을 보니 제가 보안그룹을 RDS 거를 가져다가 쓴 건지 3306만 뚫려있더군요? 그래서 443으로 전체 ip로 열어주었습니다.
다시 접속해 보니
접속은 되는데 빨간 줄이 그어져 있네요. 그래서 쭈욱 살펴보니...
제가 도메인은 django-zero라고 만들었는데 인증서에는 django-devops라고 정의했지 뭡니까!!!
인증서에 연결된 도메인을 바꾸는 것보다 route53의 호스팅을 바꾸는 게 더 편해서 다시 django-devops로 서브도메인을 바꿨습니다.
자 완벽하게 https가 적용되었습니다~!!!
❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗ ❗
이대로 적용하면 우리 인스타그램이 정상적으로 동작하지 않습니다~!
콘솔 로그를 확인해보니 저희 프로젝트에서 jquery를 다운받을 때 아래와 같은 코드를 사용하는데 http를 https로 변경해주시면 됩니다.
/* 기존 */
<!-- Jquery -->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
/* https로 수정 */
<!-- Jquery -->
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
마무리하며
ssl 인증서를 적용하기 위해 route53으로 도메인을 등록하고 호스팅을 추가하고 ACM을 통해 인증서를 발급받고 아주 우당탕탕하여 https를 적용했습니다. AWS콘솔에서 진행하는 게 많아서 글로 설명이 많이 어렵네요. 번거롭지만 자세한 사항은 유튭 영상을 확인부탁드립니다. ㅎㅎ
이번 시간에 저희가 만든 서버구조는 아래와 같습니다.
의도치 않게 DNS와 ALB까지 사용한 큰 구조가 되었네요 ㅎㅎ SSL을 사용하기 위해서는 도메인이 필요하니 아마 가장 쉽게 SSL을 적용할 수 있는 방법이 아닐까 싶습니다. 단, 다른 곳에서 도메인을 구입하신 분은 AWS Route53에서 다른 도메인을 지원해주지 않기 때문에 다른 방법으로 SSL을 등록해야 합니다. 😅
이렇게 이번시간에는 SSL 이론과 적용에 대해서 진행해 보았습니다. 다음시간은 원래 cache에 대한 것인데, 이것은 뭔가 devops의 영역과 코딩의 영역이 섞여있네요 🤔🤔🤔
devops영역은 이 정도로 하고 이제 다시 백엔드 코딩으로 넘어가도 좋지 않을까 싶습니다. 원래 생각했던 고오오급 기능들을 구현하면서 머리를 좀 써보는 내용을 다루지 않을까 싶군요 ^^ 고생하셨습니다 😀