Skip to content

14.9 SSL certificate management

บทนี้อธิบายตำแหน่งของ SSL certificate, วิธีตรวจสอบ expiry, การต่ออายุ, และการ deploy cert ใหม่

ตำแหน่ง SSL certificate

ServicePath บน hostโดเมน
vtrc-api host/home/dev/vtrc-api/cert/vtrc.redcross.or.th, vtrcapi.redcross.or.th, vtrcbackoffice.redcross.or.th
cu-central-api host/home/dev/cu-central-api/cert/centralizeapi.redcross.or.th
vtrc-rc-backoffice host/home/dev/vtrc-rc-backoffice/cert/vtrcbackoffice.redcross.or.th (ใช้ร่วมกับ host อื่นบางครั้ง)

ไฟล์ทั่วไปใน directory:

cert/
├── <domain>.crt          ←── certificate
├── <domain>.key          ←── private key
├── intermediate.crt      ←── intermediate CA
└── fullchain.crt         ←── cert + intermediate (ใช้กับ nginx)

การตรวจสอบ expiry date

ตรวจจากภายนอก (ผ่าน network)

bash
# ใช้ openssl ตรวจ cert ที่ server ส่ง
echo | openssl s_client -servername vtrc.redcross.or.th -connect vtrc.redcross.or.th:443 2>/dev/null | openssl x509 -noout -dates

ผลลัพธ์:

notBefore=Jan  1 00:00:00 2025 GMT
notAfter=Jan  1 23:59:59 2026 GMT

ตรวจจากไฟล์ cert

bash
openssl x509 -in /home/dev/vtrc-api/cert/vtrc.redcross.or.th.crt -noout -dates
openssl x509 -in /home/dev/vtrc-api/cert/vtrc.redcross.or.th.crt -noout -subject -issuer

สคริปต์ตรวจ cert ทุกโดเมน

bash
cat > /usr/local/bin/check-certs.sh <<'EOF'
#!/bin/bash
DOMAINS=(
  "vtrc.redcross.or.th"
  "vtrcapi.redcross.or.th"
  "vtrcbackoffice.redcross.or.th"
  "uat-vtrc.redcross.or.th"
  "uat-vtrcapi.redcross.or.th"
  "uat-vtrcbackoffice.redcross.or.th"
  "centralizeapi.redcross.or.th"
)

for domain in "${DOMAINS[@]}"; do
  expiry=$(echo | openssl s_client -servername "$domain" -connect "$domain:443 2>/dev/null" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
  if [ -z "$expiry" ]; then
    echo "$domain — ไม่สามารถตรวจได้"
  else
    expiry_epoch=$(date -d "$expiry" +%s)
    now_epoch=$(date +%s)
    days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
    if [ $days_left -lt 30 ]; then
      echo "$domain — จะหมดอายุใน $days_left วัน (วันที่ $expiry)"
    else
      echo "$domain — ใช้งานได้ (เหลือ $days_left วัน)"
    fi
  fi
done
EOF
chmod +x /usr/local/bin/check-certs.sh

เพิ่มใน cron:

bash
# ทุกวันที่ 1 ของเดือน
0 0 1 * * /usr/local/bin/check-certs.sh | mail -s "VTRC SSL cert check" ops@redcross.or.th

การต่ออายุ certificate

VTRC ใช้ commercial certificate (ไม่ใช่ Let's Encrypt) — ต้องซื้อจากผู้ออกใบรับรอง

ขั้นตอนทั่วไป

  1. สร้าง CSR (Certificate Signing Request)
bash
# สร้าง private key (ถ้ายังไม่มี)
openssl genrsa -out vtrc.redcross.or.th.key 2048

# สร้าง CSR
openssl req -new -key vtrc.redcross.or.th.key -out vtrc.redcross.or.th.csr -sha256

# กรอกข้อมูล
Country Name: TH
State or Province: Bangkok
Locality: Bangkok
Organization: Thai Red Cross Society
Organizational Unit: IT
Common Name: vtrc.redcross.or.th
Email: ops@redcross.or.th
  1. ส่ง CSR ให้ผู้ออกใบรับรอง

  2. รับ cert กลับมา — มักได้เป็นไฟล์ .crt และ intermediate CA

  3. สร้าง fullchain

bash
cat vtrc.redcross.or.th.crt intermediate.crt > fullchain.crt
  1. Deploy ตามขั้นตอนด้านล่าง

การ deploy cert ใหม่

สำรอง cert เดิม

bash
cd /home/dev/vtrc-api/cert/
mkdir backup-$(date +%Y%m%d)
cp *.crt *.key backup-$(date +%Y%m%d)/

คัดลอก cert ใหม่

bash
# คัดลอกไฟล์ใหม่เข้า directory
cp /path/to/new/vtrc.redcross.or.th.crt /home/dev/vtrc-api/cert/
cp /path/to/new/vtrc.redcross.or.th.key /home/dev/vtrc-api/cert/
cp /path/to/new/fullchain.crt /home/dev/vtrc-api/cert/

# ตั้ง permission
chmod 644 /home/dev/vtrc-api/cert/*.crt
chmod 600 /home/dev/vtrc-api/cert/*.key
chown root:root /home/dev/vtrc-api/cert/*

ทดสอบ config nginx

bash
docker exec vtrc-web-server nginx -t

ถ้า error cannot load certificate แสดงว่า path ใน nginx config ไม่ตรง — ตรวจ ./nginx/default.conf

Reload nginx

bash
docker exec vtrc-web-server nginx -s reload

ตรวจสอบว่า cert ใหม่ใช้งาน

bash
echo | openssl s_client -servername vtrc.redcross.or.th -connect vtrc.redcross.or.th:443 2>/dev/null | openssl x509 -noout -dates

วันที่ notAfter ควรเป็นวันที่ใหม่

การตรวจสอบ nginx SSL config

nginx config อ้างถึง cert path:

nginx
server {
    listen 443 ssl;
    server_name vtrc.redcross.or.th;

    ssl_certificate     /etc/nginx/cert/fullchain.crt;
    ssl_certificate_key /etc/nginx/cert/vtrc.redcross.or.th.key;
    
    # ...
}

path /etc/nginx/cert/ ใน container map กับ /home/dev/vtrc-api/cert/ บน host ผ่าน volume mount ใน docker-compose

ตรวจสอบ TLS version

bash
# ทดสอบ TLS 1.0 (ควร fail)
openssl s_client -connect vtrc.redcross.or.th:443 -tls1

# ทดสอบ TLS 1.2 (ควรผ่าน)
openssl s_client -connect vtrc.redcross.or.th:443 -tls1_2

# ทดสอบ TLS 1.3
openssl s_client -connect vtrc.redcross.or.th:443 -tls1_3

ตรวจสอบ cipher

bash
nmap --script ssl-enum-ciphers -p 443 vtrc.redcross.or.th

หรือใช้ tool ออนไลน์เช่น SSL Labs (แต่ต้องระวังการเปิดเผยข้อมูล)

ปัญหาที่พบบ่อย

1. nginx ไม่ reload

อาการ: nginx -t ผ่าน แต่ nginx -s reload แฮงค์

สาเหตุ: nginx กำลังประมวลผล connection ที่ยาวนาน (เช่น PDF generation)

แก้:

bash
# รอสักครู่แล้วลองอีกครั้ง
sleep 30
docker exec vtrc-web-server nginx -s reload

# ถ้ายังไม่ได้ ต้อง restart nginx (จะกระทบผู้ใช้ชั่วคราว)
docker restart vtrc-web-server

2. Browser ยังขึ้น warning หลัง deploy cert ใหม่

สาเหตุที่เป็นไปได้:

  • Browser cache cert เดิม — ลองใน incognito
  • Intermediate cert ไม่ครบ — ตรวจ fullchain
  • Cert ไม่ตรง domain — ตรวจ SAN
bash
# ดู SAN ที่รองรับ
openssl x509 -in /home/dev/vtrc-api/cert/vtrc.redcross.or.th.crt -noout -text | grep -A 1 "Subject Alternative Name"

3. Cert chain ไม่ครบ

อาการ: browser บางตัว (เช่น Android เก่า) ไม่ยอมรับ cert

ตรวจ chain:

bash
echo | openssl s_client -servername vtrc.redcross.or.th -connect vtrc.redcross.or.th:443 -showcerts 2>/dev/null | grep -c "BEGIN CERTIFICATE"

ถ้าได้ 1 แสดงว่าส่งเฉพาะ leaf cert — ต้องใช้ fullchain.crt ที่รวม intermediate

4. Private key ไม่ตรงกับ cert

bash
# เปรียบเทียบ modulus ของ key และ cert
openssl x509 -noout -modulus -in vtrc.redcross.or.th.crt | openssl md5
openssl rsa  -noout -modulus -in vtrc.redcross.or.th.key | openssl md5

ถ้า hash ไม่ตรง แสดงว่า key ผิด

การ deploy cert หลายโดเมนพร้อมกัน

ถ้าใช้ SAN cert (multi-domain) ต้อง deploy ทุก host ที่ใช้ cert นั้น:

bash
# สคริปต์ deploy หลาย host
HOSTS=("vtrc-api-host" "cu-central-host" "backoffice-host")

for host in "${HOSTS[@]}"; do
  echo "Deploying to $host..."
  scp /path/to/new/cert/* dev@$host:/tmp/new-cert/
  ssh dev@$host "sudo cp /tmp/new-cert/* /home/dev/vtrc-api/cert/ && sudo docker exec vtrc-web-server nginx -s reload"
done

แผนระยะยาว

  1. เปลี่ยนไปใช้ Let's Encrypt สำหรับ domain ที่เป็น public — ฟรี และ auto-renew
  2. ใช้ certbot ใน nginx container สำหรับ auto-renewal
  3. สร้าง alert ล่วงหน้า 30 วัน ก่อน cert หมดอายุ
  4. จัดทำ inventory ของ cert ทุกใบ พร้อม expiry date

Checklist การจัดการ cert

  • [ ] รู้ expiry date ของ cert ทุกใบ
  • [ ] มี alert ก่อนหมดอายุ 30 วัน
  • [ ] มี backup ของ cert + key ในที่ปลอดภัย
  • [ ] private key ไม่ถูก commit ใน git
  • [ ] ทดสอบ cert ใน UAT ก่อน deploy production
  • [ ] มี rollback plan (cert + key เดิมเก็บไว้)