Setup nginx-ingress with dns-01 with cloudflare

การ setup ingress ที่มี ssl/tls certificate ให้ใช้งานถูกต้องนั้น โดยปกติแล้วเราจะใช้ cert-manager ร่วมกับ Letsencrypt (ACME protocol) ในการยืนยันความเป็นเจ้าของโดเมนและให้ letsencrypt ออก (free) certificate ให้โดเมนที่เราต้องการ ข้อดีของการใช้ letsencrypt ก็คือ สามารถทำให้การจัดการ certificate นั้นเป็นไปอย่างอัตโนมัติได้ โดยปกติแล้วจะทำการยืนยันความเป็นเจ้าของโดเมน ด้วยการใช้ ACME protocol ซึ่งจะมีการ challenge ท้าทายมาให้เรา response ตอบสนองต่อการท้าทายนั้นให้ถูกต้อง

การตรวจสอบความเป็นเจ้าของโดเมนทำได้ 2 วิธีก็คือ

HTTP-01

แนวคิดของการ challenge นี้คือ ACME server จะให้เรานำ content ของไฟล์หนึ่ง มาวางไว้บน URL (สุ่มทั้งคู่) หนึ่งภายใต้โดเมนที่เราต้องการยืนยันความเป็นเจ้าของ หากเราทำได้ก็จะได้รับ certificate มา เช่น

http://your.domain.com/.well-known/acme-challenge/RixwXHspFEp0TkDZKAAiyYtMbwOycoSo1y2v6ws2B3I

ในการ response ต่อ challenge นี้ นอกจากที่เราจะจดโดเมนแล้ว เรายังต้องทำอะไรอีกหลายอย่าง

  • มี web server ไว้รองรับ http request ที่จะถูกเรียกมา (.well-known/…..)
  • พร้อมแก้ไข url ที่ challenge มา ใส่ content ให้ถูกต้องบน url ที่กำหนด – ปกติก็จะใช้เครื่องมือช่วย เช่น certbot + nginx plugin หรือ cert-manager
  • web server รองรับ challenge http request จากทาง ACME server ที่จะส่งมาเพื่อตรวจเช็คความถูกต้อง กรณีทั่วไปก็จะทำได้ไม่ติดปัญหา แต่ก็มีในบาง environment ที่เราไม่สามารถจัดหาสิ่งนี้ได้ เช่น
    • web server อยู่ในวงปิด เข้าถึงจากข้างนอกไม่ได้ (แต่อยากได้ certificate ที่มีการ sign โดย CA จริงๆ อย่างถูกต้อง)
    • web server เปิดให้เข้าได้ แต่มี firewall ป้องกัน traffic บางอย่าง (เช่น block traffic ต่างประเทศ)

กรณีที่เราไม่สามารถ accept HTTP-01 challenge ได้ก็จะทำให้เราไม่สามารถยืนยันความเป็นเจ้าของด้วยท่ามาตรฐานนี้ได้ ทางออกของเราคือ challenge ประเภทที่ 2 หรือ DNS-01 challenge

หลักการก็จะคล้ายๆ กันก็คือ ถ้าคุณเป็นเจ้าของ domain นี้จริง คุณก็จะต้องสามารถแก้ไข DNS record ตามที่เราตั้งโจทย์ให้ได้

โดยโจทย์ที่มีให้คือ เราจะต้องสร้าง TXT record ที่มี key เป็น _acme_challenge (.your.domain.com) และ value จะเป็นค่าสุ่มที่ ACME server ส่งมา

วิธีพื้นๆ ที่เราจะยืนยันแบบบ้านๆ ก็คือใช้ certbot cli ระบุ challenge type เป็น DNS-01 แล้วเอาค่าที่ได้มาไปกรอกในหน้าจัดการ dns ของโดเมนของเรา เช่น

ops@fe:~$ sudo certbot -vd dns-01.ni11.com --manual --preferred-challenges dns certonly
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Requesting a certificate for dns-01.ni11.com
Performing the following challenges:
dns-01 challenge for dns-01.ni11.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.dns-01.ni11.com.

with the following value:

vXZOj5Stf2P1GOYum18Lc35_onqKk8xr2uv18Yrody8

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.dns-01.ni11.com.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

การ accept challenge ก็คือ เราจะต้องเอา value ที่ server ส่งมา (vXZOj5Stf2P1GOYum18Lc35_onqKk8xr2uv18Yrody8) ไปใส่ไว้ใน TXT record ที่ key _acme-challenge.dns-01 ก่อน แล้วค่อยกด Enter เพื่อให้ ACME server ไปตรวจสอบ dns record ที่เราสร้างไว้ หากถูกต้องก็จะได้รับ certificate มาเก็บไว้ในเครื่อง

ส่วนวิธีล่าสุดที่ตอนแรกเลี่ยงมาตลอด (กรณี environment เป็น kubernetes และใช้ nginx-ingress ร่วมกับ cert-manager – ตัดภาพมาเลยทีเดียว) ก็คือการใช้ dns-01 challenge ร่วมกับ cloudflare ที่เป็น dns server plugin ที่ใช้ API เชื่อมต่อทำ automation ได้

(เมินมานานเพราะชอบยำเองซะมากกว่า) แต่ cloudflare เขาก็แข็งและดีจริง ที่สำคัญไม่มีค่าใช้จ่ายในการให้บริการ dns service และ API key ดังนั้นถ้าอยู่ใน environment ที่ทำ http-01 ไม่ได้ ก็ใช้ทางนี้เป็นทางรอดเสียเลย

(ต่อวันหลัง หมดเวลาโม้แล้ว)

แปะน้ำจิ้มไว้ก่อน