macOS+nginx에 Let’s Encrypt를 통해 HTTPS적용하기

블로그를 운영하고 있는 macOS+nginx에 HTTPS적용을 위하여 무료 인증 기관인 Let’s Encrypt를 이용해보기로 했다.
인터넷에 보면, Linux계열의 OS에서 Let’s Encrypt를 적용하는 방법은 상당히 많은 데 반해 Mac의 경우에는 그렇지 않아서 약간의 어려움을 겪었다.

설치 방법은 다음과 같다.

  1. 먼저 필요한 소프트웨어로는 git, python, pip가 있다.
    이들을 각각 설치해야만 한다. (git의 경우에는 Xcode CommandLine Tools를 설치하면 해결되고, 파이썬과 PIP의 경우에는 내 기준으로 별도의 설치가 필요하지는 않았다. macOS Sierra)
  2. virtualenv 를 설치해야 한다. 이 부분에 대한 설치는 다음 링크로 갈음한다.
    http://exponential.io/blog/2015/02/10/install-virtualenv-and-virtualenvwrapper-on-mac-os-x/
  3. Git을 통해 Let’s Encrypt를 Clone 받는다.
    git clone https://github.com/letsencrypt/letsencrypt
  4. 파이썬이 제대로 설치되어 있다는 가정하에, Clone받은 디렉터리 안으로 들어가서 다음 명령어를 입력한다.
    ./letsencrypt-auto certonly -a manual --rsa-key-size 4096 -d {your_domain} -d {www.your_domain} --debug

    저기에서 {your_domain} 에 각각 본인의 도메인을 입력하면 된다. (www를 사용하지 않는 경우에는 -d www.your_domain 을 삭제하면 된다.)
    macOS의 경우에는 –debug flag를 주고 실행하지 않으면, 동작하지 않는다.

  5. Manual 옵션을 주고 실행하였기 때문에, 콘솔에 이메일 주소, 각종 동의 등이 그래픽 없이 바로 나타날 것이다. 주의하여 읽어보고 필요한 정보를 입력하면 된다.
  6. 여기에서부터가 중요한데, 몇번의 동의를 거치고 나면, 이상한 숫자와 문자 조합의 키가 본인의 도메인 뒤에 추가되어 있는 것이 나타날 것이다.
    예를 들면, http://jung2.net/.well-known/acme-challenge/adfj~~~
    의 형태이다.
    Let’s Encrypt에서 위 주소로 GET요청을 보냈을 때, 텍스트 설명 아래에 있는 키값을 리턴받겠다는 의미이다.
    위 그림과 같은 안내문구가 나타나게 되는데, 텍스트로 복사하여 옮기면 다음과 같다.

    Make sure your web server displays the following content at
    http://test.com/.well-known/acme-challenge/hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4 before continuing:
    
    hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4.egXrXNstHP7VwLbvuHPeaPGWQ_jW4oiotzmh-LUVTyc
    
    If you don't have HTTP server configured, you can run the following
    command on the target server (as root):
    
    mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
    cd /tmp/certbot/public_html
    printf "%s" hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4.egXrXNstHP7VwLbvuHPeaPGWQ_jW4oiotzmh-LUVTyc > .well-known/acme-challenge/hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4
    # run only once per server:
    $(command -v python2 || command -v python2.7 || command -v python2.6) -c \
    "import BaseHTTPServer, SimpleHTTPServer; \
    s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
    s.serve_forever()"

    위 텍스트에서 설명하듯,
    http://test.com/.well-known/acme-challenge/hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4
    의 URL에 GET요청을 날릴 경우,
    hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4.egXrXNstHP7VwLbvuHPeaPGWQ_jW4oiotzmh-LUVTyc
    의 키를 응답으로 받아야만 인증서가 발급된다는 이야기이다.

  7. 이를 해결하기 위해, 웹서버의 루트 디렉터리에 .well-known 디렉터리를 만들고, 하위 디렉터리로 acme-challenge를 만들어야 함을 의미한다.
  8. 디렉터리를 생성하고 나면, 위 URL의 http://test.com/.well-known/acme-challenge/아래에 hEObuf8SZnG13BAGpXgmcpfDtLq7X1ohRivzwXP7Zd4 파일을 생성하고 내용으로 키값을 넣어주면 된다. (vim을 이용하면 된다.)
  9. 만약, www.test.com도 추가하였다면 위의 작업을 한번 더 진행하게 된다.
  10. 모든 작업이 완료되고 나서, enter를 누르게 되면 Let’s Encrypt에서 자동으로 /etc/letsencrypt/live/{your_domain}/아래에 인증서를 root 권한으로 생성한다.

여기까지 하면, Let’s Encrypt를 통해서 SSL인증서를 macOS에 발급받는 과정이 모두 완료된다.

/etc/letsencrypt/live 의 하위 디렉터리는 root권한이 있어야만 조회가 가능하며, sudo tcsh를 통해서 root권한으로 변경할 수 있다. 또한, 생성된 인증서는 cert.pem, chain.pem, fullchain.pem, privkey.pem이다.

이제 Let’s Encrypt를 통해 발급받은 인증서를 가지고, nginx에 적용하면, 모든 과정이 완료된다.

nginx.conf 파일에 설정을 가하는 방법은 OpenSSL과 nginx 버전에 따라 조금씩 상이하므로, 링크를 통해 각자의 버전 환경을 입력하는 것이 가장 간편하다.
Mozila SSL Configuration Generator를 통해 생성된 nginx.conf 파일은 아래와 같다.

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/letsencrypt/live/{your_domain}/fullchain.pem;
    ssl_certificate_key/etc/letsencrypt/live/{your_domain}/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /etc/letsencrypt/live/{your_domain}/dhparam.pem;

    # intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;

    ## verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate/etc/letsencrypt/live/{your_domain}/chain.pem;

    # resolver using Google Public DNS
    resolver 8.8.8.8 8.8.4.4 valid=86400;
    resolver_timeout 10;
}

저기에서 각자의 부분을 조금씩 수정해주면 되는데, 위 설정 파일에서 {your_domain} 부분을 수정하면 된다.
그리고 중간에 보면, ssl_dhparam 부분이 보이는데 이는 아래와 같은 커멘드로 생성하면 된다.

sudo tcsh
#enter your root passwd.
cd /etc/letsencrypt/live/{your_domain}/
#if you want to check your openssl version
# use 'opsenssl version'
openssl dhparam -out dhparam.pem 2048

이 설정을 모두 적용하고 나서 nginx를 reload한 후, 사이트에 들어가게 되면, SSL이 적용된 https 를 아래 그림처럼 확인할 수 있다.

발급받은 Let’s Encrypt 인증서는 90일 마다 새로이 갱신해주어야만 한다.
갱신은 아래 명령어로 수행하면 된다.

sudo ./letsencrypt-auto certonly --renew-by-default --webroot --webroot-path {your_webserver_root_directory_path} --email {your_email} --rsa-key-size 4096 -d {your_domain} -d {your_domain_2}

 

댓글 남기기