리눅스 Ubuntu 16.04/16.10에서 Let's Encrypt 설정하기(Nginx 환경)

1

무료 Let's Encrypt SSL 인증서

들어가며

Vultr의 가상 서버 호스팅(VPS)에 Ubuntu를 설치하고 워드프레스를 설치한 후에 무료 SSL 인증서인 Let's Encrypt를 설치하여 적용해보았습니다.

환경: Ubuntu 16.10, Nginx, PHP 7

Let's Encrypt를 설치하는 방법을 설명한 많은 문서가 있어서 어떤 방법을 따라야 할지 조금 고민이 되었습니다. 조금 검색을 하여 괜찮은 글을 하나 발견했습니다.

기본적인 설정에서 자동 갱신까지 모두 설명되어 있습니다. Ubuntu 16.04 환경에서의 설정 방법이지만 Ubuntu 16.10에서도 아무 문제 없이 작동합니다.

저는 Nginx virtual hosts 파일(예: /etc/nginx/sites-available/mydomain.conf)이 설정되어 있으므로 기존의 파일을 위에 링크된 글의 내용에 맞게 수정했습니다.

방법은 링크의 글대로 따라 하면 되지만, 편의상 여기에 그대로 옮겨놓겠습니다. 참고해보시기 바랍니다.

Nginx 스니핏

먼저 (각 가상 호스트 구성에서 코드 중복을 방지하기 위해) 두 개의 스니핏을 생성합니다.

다음 내용이 포함된 /etc/nginx/snippets/letsencrypt.conf 파일을 생성합니다.

location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /var/www/letsencrypt;
}

/etc/nginx/snippets/ssl.conf 파일을 생성하고 다음 내용을 붙여넣습니다.

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AESGCM:EECDH+AES;
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;

ssl_stapling on;
ssl_stapling_verify on;

add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

challenges를 위한 폴더를 생성합니다.

sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge

Nginx virtual hosts (HTTP 전용)

아직 인증서가 없으므로 도메인이 HTTP로만 작동합니다.

/etc/nginx/sites-available/mydomain.conf 파일을 생성하고 다음 내용을 복사합니다.

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name mydomain.com www.mydomain.com;

include /etc/nginx/snippets/letsencrypt.conf;

root /var/www/mydomain;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

위에서 mydomain.com은 실제 도메인 이름으로 일괄 변경해주시기 바랍니다. 루트 경로(/var/www/mydomain)도 실제 루트 경로로 수정해주어야 합니다.

이미 virtual host 파일이 있다면 위의 내용을 적절한 곳에 추가하도록 합니다.

사이트를 활성화합니다.

rm /etc/nginx/sites-enabled/default
ln -s /etc/nginx/sites-available/mydomain.conf /etc/nginx/sites-enabled/mydomain.conf

Nginx를 다시 로드합니다.

sudo systemctl reload nginx

Certbot

패키지를 설치합니다.

sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot

인증서 발급받기

인증서를 요청합니다. 실제 사이트 주소와 이메일 주소로 변경하도록 합니다.

certbot certonly --webroot --agree-tos --no-eff-email --email YOUR@EMAIL.COM -w /var/www/letsencrypt -d www.domain.com -d domain.com

파일이 /etc/letsencrypt/live/www.mydomain.com/에 저장됩니다.

Nginx virtual hosts (HTTPS 전용)

이제 도메인에 대한 인증서가 있으므로 /etc/nginx/sites-available/mydomain.conf 파일을 수정하여 https로 전환합니다. 파일의 내용을 다음 내용으로 교체합니다.

## http://mydomain.com and http://www.mydomain.com redirect to https://www.mydomain.com
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name mydomain.com www.mydomain.com;

include /etc/nginx/snippets/letsencrypt.conf;

location / {
return 301 https://www.mydomain.com$request_uri;
}
}

 

## Serves https://www.mydomain.com
server {
server_name www.mydomain.com;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server ipv6only=on;

ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
include /etc/nginx/snippets/ssl.conf;

root /var/www/mydomain;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

## https://mydomain.com redirects to https://www.mydomain.com
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mydomain.com;

ssl_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.mydomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.mydomain.com/fullchain.pem;
include /etc/nginx/snippets/ssl.conf;

location / {
return 301 https://www.mydomain.com$request_uri;
}
}

Nginx를 다시 로드합니다.

sudo systemctl reload nginx

Cron을 사용한 자동 갱신

Certbot은 30일 이내에 만료되는 모든 인증서를 갱신할 수 있으므로 자동 갱신하는 cron을 만들도록 합니다. Dry Run을 실행하여 구성이 올바른지 테스트해볼 수 있습니다.

certbot renew --dry-run

/root/letsencrypt.sh 파일을 만듭니다.

#!/bin/bash
systemctl reload nginx

# If you have other services that use the certificates:
# systemctl restart mosquitto

실행 가능하도록 권한을 설정합니다.

chmod +x /root/letsencrypt.sh

Cron을 편집합니다.

sudo crontab -e

다음 라인을 추가합니다.

20 3 * * * certbot renew --noninteractive --renew-hook /root/letsencrypt.sh

이것으로 Let's Encrypt 인증서 발급과 적용, 그리고 자동 갱신 설정이 완료되었습니다. 이제 사이트를 방문해보면 자동으로 https 링크로 변경되는 것을 확인할 수 있습니다.

Let's Encrypt를 적용한 후에는 사이트의 모든 http:// 링크를 https:// 링크로 일괄 변경해주어야 사이트 속도 저하 없이 제대로 작동하게 됩니다. 그리고 이미지 등의 링크도 모두 수정해주어야 크롬에서 주소란에 "안전함"이라는 글자와 함께 자물쇠 아이콘이 표시됩니다.

SSL Labs에서 테스트해보면 A+로 평가되네요.

SSL Labs - 리눅스 Ubuntu 16.04/16.10에서 Let's Encrypt 설정하기(Nginx 환경)

속도의 경우 약간 느려진 것 같지만 유의미한 차이는 없는 것 같습니다.

pingdom speed test 1 - 리눅스 Ubuntu 16.04/16.10에서 Let's Encrypt 설정하기(Nginx 환경)

마치며

VPS에 Linux를 설치하고 Nginx, PHP, MySQL 환경을 구축하고 워드프레스를 설치하는 작업과 Let's Encrypt 인증서를 적용하는 작업이 막연히 어렵게만 느껴졌지만 실제로 해보니 그다지 어려운 점은 없었습니다. 다만, 올바른 문서를 찾는 것이 중요한 것 같습니다.

현재 이 블로그가 호스팅되고 있는 Bluehost의 경우 모든 워드프레스 설치에 대해 무료 SSL을 제공한다고 합니다. 하지만 확인해보니 VPS 플랜은 제외된다고 하네요. 저가인 공유 플랜에서도 제공해주는 Let's Encrypt 인증서를 VPS 플랜에 적용해주지 않는 것은 선뜩 이해가 되지 않지만, 아마 '장사 속' 때문인 것 같습니다.

이 문제 때문에 호스팅이 만료되면 무료 SSL을 쉽게 적용할 수 있는 Siteground로 이전하거나 Vultr 같은 서비스를 이용해볼까도 고려해보고 있습니다. Vultr나 Digital Ocean 같은 업체의 경우 서버 관리에 대한 역량을 높여야 고려 대상이 될 수 있을 것 같습니다. 하지만 시간이 많이 남았으니까 현재 약정이 만료될 때까지 Bluehost의 정책이 바뀔지 지켜볼 생각입니다. (2017년 9월 업데이트:  언젠가 무료로 바뀔 것이라는 막연한 기대가 있었는데, 제 기대대로 최근에 VPS 플랜에서도 무료로 SSL이 제공되도록 바뀌었습니다.)

참고:

1개 댓글

  1. 자동 갱신 시 다음과 같이 Cron을 편집해도 괜찮을 것 같습니다.
    43 6 * * * certbot renew --renew-hook "systemctl reload nginx"

    https://serverfault.com/questions/790772/cron-job-for-lets-encrypt-renewal/825032

    https://stackoverflow.com/questions/42300579/letsencrypt-certbot-multiple-renew-hooks?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa

    https://serverfault.com/questions/790772/cron-job-for-lets-encrypt-renewal?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa

    --pre-hook and --post-hook hooks run before and after every renewal attempt. If you want your hook to run only after a successful renewal, use --renew-hook in a command like this.

댓글 남기기

댓글을 입력해주세요!
이름을 입력해주세요