ช่วงหลังๆ มานี่ปริมาณเว็บไซต์ที่ใช้ HTTPS เพิ่มขึ้นอย่างมาก ทั้งด้วยปัจจัยเรื่องความเป็นส่วนตัวของผู้ใช้ การแอบดักข้อมูลของรัฐบาล การที่คอมพิวเตอร์เซิร์ฟเวอร์มันเร็วขึ้นมากจนรองรับการเข้ารหัสกับผู้ใช้ทุกคนได้ จนไปถึง SSL ไม่ต้องเสียเงินอีกต่อไป ด้วยเหตุผลที่กล่าวมาข้างต้น ดังนั้นเว็บไซต์ยุคใหม่ควรจะปรับตัวและใช้ HTTPS ให้เรียบร้อยได้แล้ว วันนี้จึงจะมาสอนตั้งค่า NGINX ให้ได้ HTTPS สวยๆ แบบไม่ต้องเสียค่า Certificate สำหรับ SSL กัน แต่ก่อนอื่นต้องบอกว่านี่ไม่ใช่โพสที่จะสอนว่าเปิด Web Server ยังไงนะครับ นี่เป็นระดับผู้ที่ใช้งานเป็นแล้วพอสมควร
ขอเริ่มจากการเล่าถึงอุปกรณ์ที่จะใช้ก่อน
ตัวที่ 1 ที่จะใช้ในที่นี้ก็คือ NGINX ซึ่งเป็นโปรแกรมให้บริการ HTTP หรือ Web Server นั่นเอง หลายๆ คนคงจะคุ้นเคยกับ Apache 2 มากกว่า แต่สำหรับเหล่า Server Admin ที่ต้องการ High Performance แล้ว มักจะไปสาย NGINX กัน ในเรื่องรายละเอียดเรื่องค่ายไหนดีกว่าค่ายไหนจะไม่เล่าในที่นี้ ไปหาอ่านกันเอาเองนะครับ
ตัวที่ 2 คือ Let’s Encrypt ฮีโร่แห่ง SSL ป้ายเขียวแบบฟรีๆ เรื่องประวัติที่มาไม่ขอเล่าในที่นี้ แต่จะอธิบายการทำงานคร่าวๆ ให้ฟังดังนี้ วิธีการออก Certificate ของ Let’s Encrypt ใช้ ACME Challenge เป็นตัวยืนยันว่าเราเป็นเจ้าของ Domain ตัวจริง โดยอาศัยความน่าชื่อถือได้ของ DNS ซึ่งเวลาเรายิง Request ของ Certificate ออกไปจากเครื่อง มันก็จะสร้างไฟล์ขึ้นมาที่จะต้องเข้าผ่านทาง HTTP ได้ จากนั้นทางฝั่ง ACME ก็จะดึงไฟล์ดังกล่าวผ่านทางเว็บไซต์ และถ้าสามารถดึงข้อมูลได้ถูกต้องก็แปลว่า Server นั้นเป็นเจ้าของ Domain จริงๆ แล้วก็ออก Certificate ให้ เป็นอันจบขั้นตอน โดยอุปสรรคสำคัญของการใช้ Let’s Encrypt คือ Certificate มันมีอายุแค่ 3 เดือน ที่เขาทำอย่างนี้ก็เพื่อที่ว่าจะได้บังคับให้ Admin ทำโปรแกรมสำหรับขอ Certificate ให้เป็นอัตโนมัติ ซึ่งเป็นข้อดีมาก และผมสนับสนุนอย่างยิ่ง แต่ดันกลายเป็นความยากลำบากสำหรับมือใหม่หลายๆ คน และเป็นเหตุผลสำคัญที่ผมมานั่งเขียน Entry นี้
ตัวที่ 3 คือ Qualys SSL Labs อันนี้เอาไว้เช็คว่า SSL ของเราที่เซ็ตเอาไว้มีความแข็งแรงถูกต้องตามมาตรฐานเพียงใด มันมีข้อน่าเจ็บใจอยู่ที่ว่า ถ้าเราเช็ตให้แข็งแรงมาก มันคือการไม่สนใจวิธีการเข้ารหัสเก่าๆ ที่ไม่ปลอดภัย และนั่นหมายความว่าพวก Web Browsers รุ่นโบราณก็จะใช้งานไม่ได้นั่นเอง ซึ่งเราไม่สนใจมัน! ผู้ใช้ควรอัพเกรด Web Browser เป็นรุ่นใหม่เสมอเพื่อความปลอดภัย
ต่อมาจะขอเล่าถึงกระบวนการทำเลยก็ละกัน 🙂
ขั้นตอนต่อไปนี้จะรันบน user root นะครับ ดังนั้นไม่ต้องแปลกใจถ้าไม่เห็นคำสั่ง sudo
อย่างที่เล่าไปว่า Let’s Encrypt มันจะยืนยันความเป็นเข้าของ Domain ของเราได้ก็ต่อเมื่อมันสามารถเข้าเว็บเราผ่านทาง Domain ของเราได้ ดังนั้นเราจึงต้องทำการชี้ Domain ที่จะขอใช้งานบน DNS Provider เสียก่อน อันนี้ขอข้ามไปเพราะถือเป็น Basic
หลังจากที่ได้ทดสอบแล้วว่าโดเมนเข้ามาหา Server ได้แล้วจริงๆ ก็ให้ Webroot Path ของ NGINX ซึ่งปรกติจะอยู่ที่ “/usr/share/nginx/html” หรือไม่อย่างงั้นก็ “/var/www/html” ก็ลองไปสร้างไฟล์แล้วก็ลองเปิดดูว่ามันเข้าได้จริงๆ ให้จำเอาไว้ว่านี่เรียกว่า webroot จะต้องใช้คำนี้บ่อยหลังจากนี้ ในที่นี้ผมจะขอใช้ “/var/www/html” เป็นตัวอย่าง
จากนั้นก็ลง Let’s Encrypt ซะให้เรียบร้อย ใครจะลงด้วยท่าอะไรก็ตามสะดวก แต่ในเว็บเขาแนะนำให้ใช้ certbot ก็ลงซะ ถ้าใช้ Ubuntu รุ่นใหม่ๆ จะสามารถ apt install ได้เลย แต่ถ้าไม่ได้ใช้ก็ wget binary มารันเอาตามสภาพ ถ้าต้อง wget มาใช้แล้วอยากให้สวยงามก็เอาไฟล์ไปวางไว้ใน “/usr/sbin” หรือที่อื่นๆ ตามสะดวก นอกจากนั้นก็ให้แก้ชื่อเป็น certbot ซะ จะได้รัน certbot ได้จากทุกหนแห่ง
ต่อมาก็ทำการขอ Certificate ก็เลย บทเรียนนี้จะขอใช้โดเมน example.com เป็นตัวอย่างก็ละกัน แน่นอน เราต้องขอทั้ง example.com และ www.example.com สำหรับผู้อ่านจะต้องแก้ให้เป็น domain name ของตัวเองนะครับ อย่ามาใช้อย่างเดียวกัน และหลังจากนี้เจอ example.com ก็แก้เป็น domain ตัวเองเอาเองนะ ผมเตือนครั้งเดียวนะ โอเค สำหรับคำสั่งการขอ certificate ก็ใช้คำสั่งดังนี้
1 |
certbot certonly --webroot -w /var/www/html -d www.example.com -d example.com --renew-by-default |
มาดูรายละเอียดกัน
- certonly เป็นการบอกว่าจะขอแค่ certificate อย่างเดียว อย่ามายุ่งกับกรูเยอะ
- –webroot บอกว่า เราจะใช้การยืนยันตัวตนด้วย webroot จะได้ไม่ต้องปิด web server
- -w บอกว่า webroot path ของเราคืออะไร
- -d บอกว่า domain อะไรบ้างที่จะขอใน certificate นี้
- –renew-by-default บอกว่า ถ้ามี certificate นี้อยู่แล้ว ก็ให้ renew ขออันใหม่มาเลย
หลังจากรันคำสั่งนี้แล้ว ถ้าทุกอย่างสำเร็จด้วยดี มันก็จะขึ้น Congratulation! พร้อมรายละเอียดว่าจด CNAME ไว้ที่ domain อะไรบ้าง รวมถึงว่าเซฟ certificate ของเราเอาไว้ที่ไหน ซึ่งปรกติแล้วมันจะอยู่ที่ “/etc/letsencrypt/live/www.example.com” หรือไม่ก็อะไรใกล้เคียงกันนี้ ไปเช็คดูแล้วจด path เอาไว้ ข้างในนั้นจะมีไฟล์ที่สำคัญอยู่ก็คือ public/private key และ certificates
ต่อมาเราจะมาเปิด HTTPS ให้ NGINX กัน ในที่นี้เราจะใช้ระบบขี้งกคือ Web Server 1 ตัวจะโฮสหลาย Domain Name โดยใช้เทคนิคที่มีมาแต่โบราณคือ Virtual Hosting วิธีทำก็แสนง่าย เข้าไปที่ “/etc/nginx/sites-available” จากนั้นสร้างไฟล์ขึ้นมาใหม่เป็นชื่อ Domain ของเรา ในทีนี้คือ www.example.com จากนั้นแปะคำสั่งนี้ลงไป
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
server { server_name www.example.com; listen 80; return 301 https://www.example.com$request_uri; } server { server_name example.com; listen 80; listen 443 ssl; ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; ssl_prefer_server_ciphers on; ssl_session_cache shared:ssl_session_cache:10m; add_header Strict-Transport-Security "max-age=31536000"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; return 301 https://www.example.com$request_uri; } server { server_name www.example.com; listen 443 ssl; ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; ssl_prefer_server_ciphers on; ssl_session_cache shared:ssl_session_cache:10m; add_header Strict-Transport-Security "max-age=31536000"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; root /var/www/www.example.com; index index.html index.htm; } |
ขออธืบายดังนี้ ถ้าผู้ใช้เข้าเว็บเราทาง http://example.com หรือ http://www.example.com เราจะพาเขาไปที่ https://www.example.com และถ้าเขาเข้าทาง https://.example.com เราก็จะพาไปที่ https://www.example.com เช่นกัน แต่กรณีนี้มันเป็น HTTPS แล้ว เราจึงต้องใส่ info ของ SSL ลงไปด้วย ลองอ่าน Code ดูครับ ไม่ได้ยากนัก (มั้ง) ผมเพียงแค่รวบ example.com สำหรับ 80 และ 443 ไว้ด้วยกัน เพื่อประหยัดพื้นที่
บล็อคสุดท้ายคืออันที่เราจะใช้งานจริงๆ นั่นก็คือ https://www.example.com นั่นเอง อันนี้จะไม่ redirect ละ แต่จะมี root ออกว่า webroot path อยู่ที่ไหน ซึ่งก็ให้ไปสร้างโฟลเดอร์เอาไว้ให้เรียบร้อย หลังจากนี้เราจะอ่านไฟล์จากที่นี่แทน /var/www/html แล้วนะ
สำหรับ config นี้จะไม่มีเรื่องส่งต่อไปให้ PHP หรืออะไรนะครับ ใครจะรัน PHP ก็เอาบล็อคของ PHP มาใส่เอง ไม่อธิบายเพราะเป็น Basic เช่นกัน
ต่อมาจะเราจะต้องสร้างไฟล์ให้ ssl_dhparam ด้วย ซึ่งจะเห็นจากใน config ว่าผมเก็บไว้ที่ “/etc/nginx/ssl/dhparam.pem” ไฟล์นี้ไม่ได้มีเป็น default ดังนั้นต้องสร้างเอง โดยเริ่มจากไป
1 |
mkdir /etc/nginx/ssl |
จากนั้นตามด้วย
1 |
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 |
ใครอยากปลอดภัยจัดก็ใช้ 4096 bits ไปเลย แล้วแต่ความบ้าระห่ำ แต่เซียนหลายๆ ท่านได้บอกเอาไว้ว่า 2048 bits ก็ปลอดภัยหายห่วงไปได้อีกหลายปี จะไปเปลี่ยน CPU (อีกนิดหน่อย) ไปเพื่ออะไร
จากนั้นเราจะเปิดใช้งาน config ที่เราจะได้ขึ้นมา โดยวิธีการทำก็ง่ายๆ โดยใช้คำสั่งสร้างลิงค์ดังนี้
1 |
ln -s /etc/nginx/sites-available/www.example.com /etc/nginx/sites-enabled/www.example.com |
จากนั้น nginx ก็น่าจะพร้อมสำหรับอ่าน config ใหม่นี้แล้ว ก็ทำการ service nginx reload ซะ ถ้าทุกอย่างสวยงามสมบูรณ์ NGINX ก็จะไม่โวยวายอะไร และเราควรจะเปิดเว็บได้โดยที่เป็น HTTPS สีเขียวสวยงาม
ต่อมาก็ไปทดสอบว่ามันดีจริงที่ Qualys SSL Labs ดู ถ้า config ตามที่ว่าไว้ก็น่าจะได้ A+ กลับมา
ยินดีด้วยสำหรับผู้ที่ผ่านมาถึงจุดนี้ได้ แต่มันยังไม่จบ สิ่งต่อมาที่เราจะทำก็คือทำให้การขอ certificate เป็นไปอย่างอัตโนมัติ ในที่นี้จะขอลุยแบบง่ายๆ โดยใช้ crontab
แต่ก่อนจะเริ่ม สิ่งนึงที่ต้องระลึกไว้ก็คือ webroot เราเปลี่ยนไปแล้ว จากเดิมเราใช้ /var/www/html ซึ่งเป็นค่า default ตอนนี้กลายมาเป็น /var/www/www.example.com ทำให้คำสั่งการขอ Certificate เปลี่ยนไป ดังนี้
1 |
certbot certonly --webroot -w /var/www/www.example.com -d www.example.com -d example.com --renew-by-default |
ลองเขียนคำสั่งสำหรับขอ Certificate ใหม่แบบที่มันใช้ได้ดู ถ้าสำเร็จ ให้เซฟเก็บเอาไว้ อันนี้สำคัญจะต้องใช้
ต่อมาเพื่อความสวยงาม เราจะเขียนมันใส่ลงไฟล์ shell โดยหน้าตาข้างในไฟล์จะเป็นประมาณนี้
1 2 3 4 |
#!/bin/sh date certbot certonly --webroot -w /var/www/www.example.com -d www.example.com -d example.com --renew-by-default service nginx reload |
ผมใส่คำสั่ง date ลงไปด้วยจะได้รู้ว่ามันทำเมื่อไหร่ ต่อมาก็เซฟลงไฟล์ ของผมไว้ที่ /opt/ssl/renew.sh แล้วก็ chmod +x ซะ เรียบร้อยแล้วก็ลองรันสักที ./renew.sh และหวังว่าทุกอย่างจะสวยงาม พอรันเสร็จ ไป refresh เว็บสักครั้ง ดูซะว่า certificate เราเปลี่ยนไหม (ลืมบอกให้ดู signature ของ certificate อันก่อนหน้า)
ต่อมาคือการทำให้มันรันทุกเดือน เดือนละครั้ง โดยใส่ลงไปใน crontab ก็เข้าไปใน crontab -e ซะ จากนั้นก็เพิ่มบรรทัดใหม่เข้าไปประมาณนี้
1 |
3 4 5 * * /bin/sh /opt/ssl/renew.sh >> /opt/ssl/renew.log |
นี่เป็นการบอกว่าทุกๆ เดือนในวันที่ 5 เวลา 04:03 ให้รันคำสั่งนี้ ความจริงตัวเลขว่าจะรันเมื่อไหร่ไม่มีประเด็น ใส่อะไรก็ได้ตามใจชอบ แต่ให้พึงระลึกไว้นิดนึงว่า ถ้าเราใช้วันที่ 1 เวลาเที่ยงคืน มันจะมีกี่คนที่มา request พร้อมๆ กับเรา ดังนั้นเพื่อกระจายโหลด server ACME จึงให้ตั้งเวลาที่มันไม่ปรกติลงไป ของผมมาสายเลขสวยเลยได้ประมาณนี้ สำหรับผมให้เขียนลงไฟล์ log ด้วย จะได้ตามดูได้เวลามีปัญหา
ที่ยังเหลืออยู่ก็คือ รอดูว่าพอถึงวันเวลาที่ตั้งเอาไว้แล้วคำสั่งมันรันจริงรึเปล่า
เป็นอันเสร็จขั้นตอนการตั้งค่า NGINX กับ Let’s Encrypt ที่สมบูรณ์
ใครติดปัญหาตรงไหน หรือเจอข้อผิดพลาด หรือมีข้อแนะนำ ก็แจ้งผ่านทาง Comments เลยนะครับ
สุดท้ายก็อยากจะขอให้คนทำเว็บไซต์หันมาใช้ HTTPS กันเยอะๆ เพื่อความเป็นส่วนตัวของผู้ใช้กันนะครับ 🙂