HTTPS 적용

appti·2022년 6월 9일
0

HTTPS 적용

이전 글인 Jenkins + CodeDeploy + Nginx를 활용한 스프링 부트 Docker 이미지 무중단 배포 환경에서 진행됩니다.

Freenom & AWS Route 53

무료 도메인 사이트인 Freenom을 사용합니다.
로그인한 상태에서 진행합니다.

사용하고자 하는 도메인 명을 입력하고 Check Availabillity를 클릭합니다.

사용할 도메인 명의 우측에 있는 Get it now!를 선택합니다.

우측 상단에 Checkout을 선택합니다.
만약 Selected가 아니고 Not available!이 출력된다면 마지막 최상위 도메인(TLD)까지 입력합니다.
예를 들어 cicd-test가 아니라 cicd-test.ga로 검색하시면 됩니다.

Period를 반드시 확인해주세요.
12 Months는 무료인데 1 Year는 유료입니다.

구매까지 진행하면 되겠습니다.

구매 후 Services -> My Domains로 이동합니다.

방금 구매한 도메인을 확인할 수 있습니다.
Manage Domain을 선택합니다.

Manage Freenom DNS를 선택합니다.

다음과 같이 입력합니다.

  • Name : 해당 설정의 이름으로, 입력하지 않아도 됩니다.
  • Type : DNS에 저장되는 정보의 타입으로, A 타입을 선택해줍니다.
    • A 타입이란 A 레코드(A Record) 타입이라는 의미로, 도메인 주소와 서버의 IP 주소를 1:1로 매핑시키는 방법입니다.
      사용자가 A 레코드에 해당하는 도메인 주소(paws-api.ga)에 대한 해석을 요청하면 DNS 서버IP 주소(AWS EC2 Public IPv4) 를 리턴해줍니다.
  • TTL : DNSIP 주소가 캐시로 저장되는 시간으로, 기본값인 3600을 그대로 사용합니다.
  • Target : AWS EC2 Public IPv4 값을 입력해줍니다.

이후 설정을 위해 AWS Route 53 설정을 진행합니다.


호스팅 영역을 생성합니다.

도메인 이름에 구입했던 도메인을 입력합니다.
나머지는 기본 값으로 진행합니다.

업로드중..

레코드 유형이 NS(Name Space)인 값을 Freenom에 설정해줘야 합니다.

Manage Domain -> Management Tools -> Nameservers를 선택합니다.

Use custom nameservers (enter below)를 선택하고, Nameserver에 값을 입력합니다.

이제 Freenom에서 등록한 DNS 관련 설정을 AWS Route 53에서도 설정합니다.

  • 레코드 이름
    • 라우팅할 이름을 지정합니다.
    • 지정하지 않으면 기본 도메인 이름이 설정됩니다.
  • 레코드 유형
    • AWS EC2로 라우팅 시 IPv4로 라우팅하기 때문에 변경할 필요가 없습니다.
    • freenom의 타입도 A 타입이므로 그대로 유지하면 됩니다.
    • 다른 유형의 레코드 생성 시 해당 유형을 선택하시면 됩니다.
    • 라우팅 할 AWS EC2 IPv4 주소를 입력합니다.
    • 다른 AWS 리소스로 라우팅이 가능한 유형의 경우 별칭을 체크하면 라우팅 가능한 서비스를 선택할 수 있습니다.
  • TTL
    • DNSIP 주소가 캐시로 저장되는 시간으로, 기본값인 3600을 그대로 사용합니다.
  • 라우팅 정책
    • 트래픽에 대한 라우팅 방법을 선택합니다.
    • 상황에 따라 선택하면 되며, 변경할 필요는 없습니다.
    • 각 옵션에 대한 의미는 다음과 같습니다.
      • 단순 : 표준 DNS기능
      • 가중치 기반 : 각 리소스에 보낼 트래픽을 지정할 수 있음
      • 지리적 위치 : 지정한 위치의 가까운 사용자의 트래픽을 라우팅
      • 지연 시간 : 지연시간이 가장 짧은 리전으로 트래픽을 라우팅
      • 장애 조치 : 정상 상태일때만 트래픽을 라우팅
      • 다중값 응답 : DNSlook upIP 주소값을 다중으로 반환할 수 있도록 구성

값에 ec2 public ipv4를 입력하고, 나머지는 기본 값으로 진행하면 되겠습니다.

EC2 설정 - 기존

Let's Encrypt

TLS 인증서를 무료로 발급해주는 Let's Encrypt를 사용합니다.

Let's Encrypt의 경우 인증서 발급 시에 80 포트를 사용하므로, 이를 보안 그룹에서 허용해주어야 합니다.

sudo service nginx stop
Redirecting to /bin/systemctl restart nginx.service

Let's Encrypt가 중간에 nginx를 사용하기 때문에, nginx를 종료하는 것이 안전합니다.

# EPEL 다운로드
# let's encrypt는 certbot을 이용하며, EPEL은 certbot에 필요한 의존성을 공급하는데 필요합니다.
sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/

# 리포지토리 패키지 설치
sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm

# EPEL 활성화
sudo yum-config-manager --enable epel*

# certbot 설치
sudo yum install -y certbot python2-certbot-apache

# certbot-nginx 설치
sudo yum install certbot-nginx

# 인증서 발급
# --email : 인증서 관련 안내 이메일을 전송 받는 이메일
# -d : 도메인
sudo certbot --nginx --agree-tos --redirect --staple-ocsp --email <email> -d <도메인>

정상적으로 인증서가 발급되었다면 위 사진과 같은 로그가 출력됩니다.

주의할 점으로는 로그에서 확인할 수 있는 것처럼 인증서가 90일간 유효하다는 점입니다.

인증서가 만료되었는지는 다음 명령어로 확인할 수 있습니다.

certbot certificates

인증서가 만료되면 수작업으로 갱신을 해 줘야 하는데, 이 경우 스케쥴러를 통해 간단히 해결할 수 있습니다.

# 인증서 갱신 테스트, 성공하는지 확인한 뒤 나머지 작업 진행
sudo certbot renew --no-self-upgrade --dry-run

# 리눅스 스케쥴러인 crontab 사용
vim /etc/crontab

# /etc/crontab
30	1,13	*	*	*	root	certbot renew --no-self-upgrade
# /etc/crontab

# crontab 변경사항을 적용하기 위해 재시작
sudo systemctl restart crond

매일 1시 30분, 13시 30분에 인증서를 갱신하는 명령어가 동작하도록 했습니다.
매일 갱신하도록 한 이유는 cerbot에서 최소 매일 두 번씩은 인증서를 갱신하도록 권장하기 때문입니다.

certbot renew --no-self-upgrade

위 명령에서 사용한 옵션은 다음과 같습니다.

  • renew
    • certbot이 모든 인증서을 점검하고 만료 날짜가 얼마 남지 않은 인증서를 갱신합니다.
  • --no-self-upgrade
    • certbot만을 사용해 인증서를 갱신하도록 설정합니다.

nginx

# nginx.conf

server {
  listen       443 ssl;
  listen       [::]:443;
  server_name  cicd-test.ga;
  
  ssl_certificate /etc/letsencrypt/live/cicd-test.ga/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/cicd-test.ga/privkey.pem;
  ssl_trusted_certificate /etc/letsencrypt/live/cicd-test.ga/chain.pem;
  
  ssl_session_timeout  10m;
  ssl_stapling on;
  ssl_stapling_verify on;
  
  # Load configuration files for the default server block.
  include /etc/nginx/default.d/*.conf;
  include /etc/nginx/conf.d/service-url.inc;

  location / {
    proxy_pass $service_url;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forworded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_connect_timeout 300;
    proxy_send_timeout 600;
    send_timeout 600;
    proxy_read_timeout 300;
  }
}

위와 같이 작성해주시면 되겠습니다.

server {
    listen       80;
    listen       [::]:80;
    root         /usr/share/nginx/html;

    return 301 https://$host$request_uri;
}

HTTPS 설정을 했으므로 HTTP를 허용해줄 이유가 없습니다.
HTTP로 들어올 경우 HTTPS로 리다이렉트 하도록 설정합니다.

sudo service nginx start
Redirecting to /bin/systemctl start nginx.service

설정을 변경했다면, 중지했던 nginx를 다시 실행시킵니다.

sudo fuser -k 80/tcp
sudo fuser -k 443/tcp

nginx 실행 오류가 발생한다면, 위 명령어를 통해 프로세스를 종료한 뒤 다시 시작합니다.

이 상태에서 SSL Server Test에 접속해 사이트 테스트를 진행합니다.

그럼 다음과 같이 보안 점수가 B인 것을 확인할 수 있습니다.
정상적으로 작업이 처리되지 않았다면 접속이 불가능해 테스트에 실패하기 때문에, 정상적으로 HTTPS가 적용되었는지 확인할 수 있습니다.

실행해보면 정상적으로 HTTPS가 적용되었고, 도메인을 통해 접속하는 것을 확인할 수 있습니다.

HTTP로 요청하면 자동으로 HTTPS의 포트로 301 리다이렉트가 실행됩니다.

EC2 설정 - 개선

보안 관련 파트를 개선해보도록 하겠습니다.

AWS Route 53

CAA 레코드를 추가해줍니다.

Let's Encrypt

# 인증서 발급
# RSA 키 크기 : 기본 2048
sudo certbot --nginx --agree-tos --redirect --staple-ocsp --email <email> -d <도메인>

# 인증서 발급
# RSA 키 크기 : 4096 명시
sudo certbot --nginx --agree-tos --redirect --staple-ocsp --email <email> -d <도메인> --rsa-key-size 4096
  • RSA 키를 기본 2048에서 4096으로 변경해 새롭게 인증서를 생성합니다.

nginx

dhparam

dhparam에서 사용할 암호화 키를 생성하기 위해 OpenSSL 업데이트가 필요합니다.

# OpenSSL 버전 확인
openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017

# OpenSSL 삭제
sudo yum remove openssl -y

# OpenSSL에서 3.0 이상의 버전 다운로드
wget https://www.openssl.org/source/openssl-3.0.2.tar.gz

# 압축 해제
tar xvfz openssl-3.0.2.tar.gz

# 디렉토리 이동
cd openssl-3.0.2

# config를 위해 의존성 추가
# config시 gcc 및 perl 사용
sudo yum install gcc gcc-c++ pcre-devel zlib-devel perl wget -y
sudo yum install perl-IPC-Cmd -y
sudo yum install perl-Data-Dumper -y

# Openss config
./config

# 빌드 및 install
# sudo로 실행하지 않으면 install 도중 권한 부족으로 실패
sudo make && sudo make install

# 공유 라이브러리 캐시 설정
# Openssl의 경우 동적으로 링크된 실행 파일이기 때문에 공유 라이브러리에 완벽하게 의존적 
# Openssl의 새 버전을 설치했으므로 실행하기 위해서는 디렉토리를 설정하고 ldconfig로 공유 라이브러리 캐시를 다시 설정해야 함 
sudo ldconfig /usr/local/lib64/

# 심볼릭 링크 설정 
# 글로벌하게 Openssl을 사용하기 위함
ln -s /usr/local/bin/openssl /bin 

# dhparams 생성
# 키의 크기는 인증서와 동일한 4096
sudo openssl dhparam -out /home/ec2-user/dhparams.pem 4096

config가 정상적으로 실행되었다면 위와 같이 로그가 출력됩니다.

dhparam을 생성할 때에는 좀 많이 기다려야 합니다.

nginx.conf

server {
    listen       443 ssl;
    listen       [::]:443;
    server_name  cicd-test.ga;

    ssl_certificate /etc/letsencrypt/live/cicd-test.ga/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/cicd-test.ga/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/cicd-test.ga/chain.pem;

    ssl_session_timeout  10m;
    ssl_stapling on;
    ssl_stapling_verify on;

    ssl_protocols TLSv1.2 TLSv1.3;

    ssl_prefer_server_ciphers on;
    ssl_ciphers "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
    ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256;
    ssl_conf_command Options ServerPreference,PrioritizeChaCha,NoRenegotiation,NoResumptionOnRenegotiation;
    ssl_ecdh_curve secp384r1;

    ssl_dhparam /etc/pki/tls/dhparams.pem;

    ssl_session_cache shared:SSL:10m;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;
    include /etc/nginx/conf.d/service-url.inc;
    location / {
        proxy_pass http://localhost:8081;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forworded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_connect_timeout 300;
        proxy_send_timeout 600;
        send_timeout 600;
        proxy_read_timeout 300;
        }
        
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

위와 같이 설정합니다.
추가된 설정을 간략하게나마 살펴보도록 하겠습니다.

  • ssl_protocols
    • 기존에는 모든 TLS을 허용했기 때문에 보안 등급이 낮아졌습니다.
    • 보안이 강화된 TLS 1.2, TLS 1.3만을 허용합니다.
  • ssl-ciphers
    • Cipher Suites를 지정해 암호화 알고리즘을 지정합니다.
  • ssl_conf_command
    • 암호화 해독 시 사용할 OpenSSL의 설정을 지정합니다.
  • ssl_ecdh_curve
    • ECDHE 암호에 대한 curve를 지정합니다.
  • ssl_dhparam
    • DHE 알고리즘 키를 지정합니다.
  • add_header
    • 응답 헤더를 추가합니다.
    • HTTP 대신 HTTPS만을 사용하여 통신해야한다고 웹 사이트가 브라우저에 알리는 보안 기능입니다.
    • includeSubDomains를 통해 서브 도메인 또한 해당 헤더가 추가되도록 설정했습니다.
    • always를 통해 항상 해당 헤더가 추가되도록 설정했습니다.

테스트 시 위와 같이 보안 등급이 A+인 것을 확인할 수 있습니다.

profile
안녕하세요

0개의 댓글