Setup Let's Encrypt กับ nGINX

Reference: https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71

Setup certbot กับ nginx

เราจะต้องมี certbot runคู่กับ nginx ก่อน certbot เป็น tool ของ Electronic Frontier Foundation สำหรับจัดการ Let's Encrypt certificates

version: '3'
services:
  nginx:
    image: nginx:1.15-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
      
  certbot:
    image: certbot/certbot
    volumes:
	  - ./data/certbot/conf:/etc/letsencrypt
	  - ./data/certbot/www:/var/www/certbot
ตัวอย่าง docker-compose.yml ที่มี certbot

สังเกตุว่ามีการ mapping volumes ของทั้ง nginx และ certbot ให้ shared กันด้วย

Add "acme-challenge" path

เพิ่ม section ต่อไปนี้ลงใน server section ของ service ที่เราต้องการให้มี certificate แต่เป็น port 80!

location /.well-known/acme-challenge/ {
    root /var/www/certbot;
}

เช่น

server {
  rewrite_log off;

  listen 80;

  charset utf-8;
  server_name example.org;

  client_max_body_size 1m;

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

  location /.well-known/acme-challenge/ {
    root /var/www/certbot;
  }
}

จากนั้นเพิ่ม section นี้ลงใน server ที่เป็น port 443! สังเกตุว่าตรง URL จะมีชื่อ domain ของเราอยู่ด้วย (ในที่นี้คือ example.org) ต้องแก้ให้ตรงกับ domain ที่เราจะขอ

ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;

และ (optional) best-practice HTTPS configuration ของ Let's Encrypt เอง

include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

เช่น

server {
 rewrite_log off;

 listen 443 ssl http2;

 charset utf-8;
 server_name example.org;

 location / {
   root  /app;
   index home.html;
 }

 ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
 ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;

 include /etc/letsencrypt/options-ssl-nginx.conf;
 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

Create dummy certificate to start nginx

มี script สำหรับสร้าง dummy certificate (เป็น bash) download ได้จาก

curl -L https://raw.githubusercontent.com/wmnnd/nginx-certbot/master/init-letsencrypt.sh > init-letsencrypt.sh

แต่ต้องแก้ file init-letsencrypt.sh ก่อน มี 3 fields ที่ต้องแก้คือ

domains=(site.yourdomain.uri)
data_path="/data/certbot" # certbot shared path
email="admin@yourdomain.uri"  # valid email address

Note: ถึงแม้ว่า domains จะเป็น array แต่ควรทำทีละ site เพราะเคยเจอปัญหาทำหลายๆอันแล้ว script มัน run ไม่ถูก

run init-letsencrypt.sh เพื่อ start nginx และ download certificate จาก Let's Encrypt!

Auto renewal

เพิ่ม script ดังต่อไปนี้ที่ certbot เพื่อให้ check certificate renewal ทุกๆ 12 ชั่วโมง (ตามคำแนะนำของ Let's Encrypt)

entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

และเพิ่ม script นี้ใน nginx เพื่อให้ nginx reload config ทุกๆ 6 ชั่วโมง

command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

docker-compose.yml สุดท้าย หน้าตาประมาณนี้

version: '3'
services:
  nginx:
    image: nginx:1.15-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data/nginx:/etc/nginx/conf.d
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    
  certbot:
    image: certbot/certbot
    volumes:
	  - ./data/certbot/conf:/etc/letsencrypt
	  - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"