이전 글인 Jenkins + CodeDeploy + Nginx를 활용한 스프링 부트 Docker 이미지 무중단 배포 환경에서 진행됩니다.
무료 도메인 사이트인 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
: DNS
에 IP 주소
가 캐시로 저장되는 시간으로, 기본값인 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
DNS
에 IP 주소
가 캐시로 저장되는 시간으로, 기본값인 3600
을 그대로 사용합니다. DNS
기능DNS
의 look up
시 IP 주소
값을 다중으로 반환할 수 있도록 구성값에 ec2 public ipv4
를 입력하고, 나머지는 기본 값으로 진행하면 되겠습니다.
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.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 리다이렉트가 실행됩니다.
보안 관련 파트를 개선해보도록 하겠습니다.
CAA 레코드
를 추가해줍니다.
# 인증서 발급
# 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으로 변경해 새롭게 인증서를 생성합니다.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
을 생성할 때에는 좀 많이 기다려야 합니다.
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+
인 것을 확인할 수 있습니다.