본문 바로가기

기술이야기

무료로 내 서버에 HTTPS 적용하기

 

0. 준비


이 글은 Let's Encrypt라는 비영리 기관에서 무료로 발급하는 인증서를 이용해 Nginx에 SSL를 적용하는 방법이다. 이 방법을 이용하면 무료로 내 서버에 https로 접근할 수 있다. 단, 사전에 아래 사항이 준비가 되어 있다는 가정 하에 진행한다.

  • CentOS7 리눅스 서버
    • Nginx
    • openssl 1.1.1 이상
  • 도메인 발급

- Centos7 서버를 구축하면 openssl이 기본적으로 설치되어 있다. 하지만 대부분 버전이 1.0.2일 것이다. openssl 1.0.2 버전은 Let's Encrypt가 발급하는 인증서를 만료된 인증서로 간주하기 때문에 정상 동작하기 어렵다. 또한 해당 버전은 상위 TLS 버전(1.3)을 지원하지 않는다. 따라서 별도로 openssl 1.1.1 버전 이상을 설치해야 한다.(https://www.openssl.org/blog/blog/2021/09/13/LetsEncryptRootCertExpire/ 참고)

- Nginx을  yum이나 apt-get을 이용하여 설치할 경우 openssl이 빌트인 되어 있다. 따라서 "nginx -V" 명령어를 이용해 openssl 버전을 확인할 수 있다. 만약 빌트인 된 openssl의 버전이 1.1.1 미만일 경우 nginx도 재설치가 필요하다.

 * 설치할 때 openssl 버전 설정을 직접 할 수 있도록  컴파일 방식으로 설치한다. 컴파일 수행 시 "--with-openssl={openssl 1.1.1 디렉토리}" 옵션을 명시한다.

- 도메인은 freenom 사이트에서 무료로 발급 받을 수 있다.

 

 

 

1. 설치


Let's Encrypt 인증서는 certbot이라는 프로그램(데몬)을 이용해서 쉽게 발급받을 수 있다. 현재 certbot을 가장 쉽게 설치할 수 있는 방법은 EPEL Repository를 이용하는 것이다. 

 * certbot: let's encrypt 기관의 인증서를 자동으로 발급/갱신해주는 봇(데몬)

 * EPEL Repository: Extra Packages of Enterprise Linux의 약자로 정식 패키지에 없는 프로그램을 설치하고 싶을 때 EPEL 저장소를 이용해 설치한다. Certbot 또한 Centos7 정식 패키지에 없는 프로그램이기 때문에 EPEL 저장소를 이용해 설치한다.

 

sudo yum -y install epel-release
sudo yum -y install certbot-nginx

 

 

 

2. Nginx 설정


인증서 발급 중 Certbot과 내 서버와 통신하는 과정이 있다. 이를 위해 사전에 요청을 정상적으로 받을 수 있도록 설정이 필요하다. 이미 되어 있다면 건너뛰고 다음 과정을 진행해도 무방하다.

 

sudo vi /etc/nginx/nginx.conf

 

편집기로 nginx.conf  파일을 열어 server 블록을 아래 내용으로 수정한다.

 

server_name www.example.com; # 본인 도메인으로 세팅

 

수정 후 nginx을 재기동한다.

 

sudo nginx -t # nginx 문법 검증
sudo systemctl reload nginx

 

* 만약 서버에 방화벽이 활성화 되어 있다면 80, 443 포트를 개방해야 한다.

 

 

 

 

3. 인증서 발급


인증서 발급은 간단하다. 아래 명령어만 실행하면 된다.

sudo certbot --nginx -d www.example.com

 

만약 Certbot을 처음 실행할 경우 Let's Encrypt 소식지를 받을 이메일을 입력한다.

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel):

 

이후 정상 발급을 위해 ACME 프로토콜을 이용하여 CA기관의 서버에 등록할 것인지 동의를 하는 문구가 출력된다. 

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o:

 

* ACME 프로토콜을 이용하여 ACME Agent와 CA기관이 통신하는 과정은 아래와 같다. 

ACME 통신 과정, 구글이미지 참조

 

 

 

동의까지 마쳤으면 정상적으로 인증서가 발급되었다는 문구가 출력된다.

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/www.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/www.example.com/privkey.pem
   Your certificate will expire on 2022-06-30. To obtain a new or
   tweaked version of this certificate in the future, simply run
   certbot again. To non-interactively renew *all* of your
   certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

 

만약 정상적으로 발급되지 않을 경우 /var/log/letsencrypt/ 아래에 위치한 log 파일을 확인한다. 다음은 발급 중 만날 수 있는 에러이다.

 

1. Errno bad handshake

 - 해결1: 서버의 openssl 버전이 1.1.1 미만인지 확인한다. 미만일 경우 1.1.1 이상의 버전으로 별도로 설치해야 한다.

 - 해결2: --no-verify-ssl 옵션을 추가하여 certbot certonly --nginx 명령어를 실행한다.

2. TypeError: __str__ returned non-string (type Error)

 - 해결: pip을 이용해 urllib3와 requests를 설치한다. e.g. sudo pip install urllib3 requests

 

 

* 인증서 갱신

Let's Encrypt 인증서는 90일 단기 인증서이기 때문에 주기적으로 갱신 요청을 해야 한다. 아래 crontab을 이용하면 자동으로 갱신 요청을 보낼 수 있다.

 

sudo crontab -e 

0 0 1 * * /usr/bin/certbot renew --renew-hook "/bin/systemctl reload nginx"

 

 

 

4. 발급된 인증서를 nginx에 적용


정상적으로 인증서 발급이 되었다면 /etc/letsencrypt/live/도메인명/ 위치에 아래 4개의 파일이 생성된 것을 확인할 수 있다. 

 

1. cert.pem: 요청한 도메인의 인증서 (public key)

2. chain.pem: Let's encrypt의 중간 인증서

3. privkey.pem:  개인 키(RSA 방식)

4. fullchain.pem: cert.pem과 chain.pem을 합친 파일

 

이 중 privkey.pem, fullchain.pem 이 두 개의 파일을 nginx에 적용한다. 

listen 443 ssl;

ssl_certificate     /etc/letsencrypt/live/www.example.com/fullchain.pem; # 인증 체인 설정
ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; # 개인 키 설정

 

이후 nginx을 재기동하면 https로 내 도메인을 정상 접근할 수 있다.

 

 

 

5. 확인


 

https://www.ssllabs.com/ 이 사이트에 접속하면 내 서버가 정삭적으로 SSL이 적용되었는지 체크할 수 있다. 또한 아래처럼 내 서버의 SSL 등급을 확인할 수 있다.

 

SSL 등급

 

처음 SSL을 적용하면 대부분 B 등급이 나온다. 만약 A 등급으로 높이고 싶다면 아래 작업을 추가로 진행해야 된다.

 

 

1. Diffie-Hellman Parameters 강화

- DiffieHellman 키 교환은 암호화되지 않는 통신망에서 비밀 키를 공유할 수 있도록 도와주는 방식이다.  SSL Handshake 과정 중 키 교환할 때 이를 이용한다. 만약 Diffie-Hellman Parameter의 길이가 1024 비트일 경우 B등급을 나올 수 있다. A등급을 위해 아래처럼 2048 크기의 Diffie-Hellman Parameters를 생성하고 nginx에 적용한다.

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

ssl_dhparam /etc/ssl/certs/dhparam.pem; #vim nginx.conf

 

2. TLS 1.1, TLS 1.2 비활성화

- 2020년 초부터 TLS 1.1, TLS 1.2는 사용하지 않고 TLS 1.3 버전만 지원한다. 만약 본인의 서버가 TLS 1.1, TLS 1.2까지 지원하도록 설정되어 있다면 등급이 낮을 수 있다.