กราบสวัสดีท่านผู้อ่านที่รักทุกท่าน วันนี้ผมจะขอมาแบ่งปันประสบการณ์และขั้นตอนการตั้ง Web Server ท่าใหม่ที่ผมเพิ่งได้เรียนรู้และทดสอบใช้งานประสบความสำเร็จเป็นอย่างดี นั่นคือการใช้ Portainer และ Cloudflare Tunnel ซึ่งผมเล็งเห็นว่าเป็นการตั้ง Web Server ควรค่าแก่การจดบันทึกและแบ่งปัน

วิธีนี้ช่วยให้เราจัดการ App ต่างๆ ในเครื่องได้โดยง่ายโดยใช้ Portainer ในการจัดการ Container ส่วนเรื่องการนำขึ้น Public Network ขึ้น Internet ให้ Cloudflare Tunnel ช่วยดูแล ส่วนสุดท้ายคือการทำให้ Web ของเราปลอดภัยตั้ง Access Control ด้วย Cloudflare Access ทั้งหมดที่ว่านี้ ไม่ต้องเขียนโปรแกรมเพิ่มเติมแต่อย่างใด

Services ทั้งหมดที่ว่ามานี้ ฟรี! ไม่มีค่าใช้จ่ายรายเดือนแต่อย่างไร แต่มีค่า Domain Name ที่จะใช้เชื่อมกับ Cloudflare ตกปีละประมาณ 300 กว่าบาท

คำเตือน! บทความนี้โคตร Technical เหมาะสำหรับผู้มีประสบการณ์ DevOps และการตั้ง Web Server เป็นแล้วเท่านั้น ผมคงไม่ลงอธิบายทุกอย่างในบทความนี้ หากอ่านแล้วเจอศัพท์ที่ไม่รู้จักแล้วยังอยากจะไปต่อ รบกวนทำการศึกษาเพิ่มเติมต่อด้วยตัวเองนะครับ 🙂


ที่มาที่ไปของโครงการ

ตัวผมเองมีงานอดิเรกเป็นการโม Raspberry Pi เล่นต่อเนื่องมาเป็นเวลาหลายปีแล้ว เอามาทำ NAS บ้าง เอามาทำ Bit Torrent Server บ้าง เอามาทำ Web Server บ้าง ซึ่งทุกวันนี้เน็ตบ้านเดี๋ยวนี้ก็แรงซะจนเอามาทำ Web Server ให้คนข้างนอกเข้ามาใช้งานสบายๆ แล้ว

โดยการทำ Web Server นี้เองคือสิ่งที่ท้าทายที่สุด เพราะเราจำเป็นจะต้องใช้ Static Public IP หรือไม่ก็ต้องใช้ DDNS เพื่อให้ User สามารถเข้าถึงผ่านทางอินเทอร์เน็ตได้ ในขณะที่เน็ตบ้านสมัยนี้ก็บังคับให้เราอยู่หลังวง NAT อีกชั้นหนึ่ง ทำให้ทั้งสองวิธีข้างต้นใช้การไม่ได้อีกต่อไป

ผมจึงได้เคยลองพยายามแก้ปัญหามาอย่างต่อเนื่อง เริ่มตั้งแต่การใช้ Reverse Proxy กับ Heroku ผ่านเข้า DDNS บน Port แปลกๆ ที่ ISP ให้มาใช้ (เพราะ Cloudflare ไม่อนุญาตให้ Cloudflare Worker วิ่งผ่าน Port เหล่านั้น จึงต้องใช้ Heroku ช่วย) แต่ผลก็คือมันช้าเพราะวิ่งข้ามโลกอ้อมไปอ้อมมา ถ้าสนใจรายละเอียเพิ่มเติม สามารถไปอ่านกันต่อได้ที่ ทำ Reverse Proxy เข้า Raspberry Pi เน็ตบ้าน ด้วย Cloudflare Workers และ Heroku

ต่อมาหลังจากเปลี่ยนมาใช้ Internet Fiber ก็ได้ IPv6 มาใช้ จากนั้นก็เลยเปลี่ยนมาใช้วิธีการ Fix IPv6 ไว้ แล้วให้ Cloudflare ชี้หา DNS Record AAAA ที่เป็น IPv6 ของเรา เรียบร้อยแล้วก็เปิด Cloudflare Proxy ให้ Network ทั้งหมดวิ่งผ่าน Cloudflare ซึ่งรองรับ IPv4 ทำให้เว็บของเราใช้งานได้ตามปรกติ วิธีนี้แก้ปัญหาเรื่องความช้าไปได้เพราะไม่ต้องวิ่งอ้อมโลกไกลแล้ว แต่ยังมีความยากเรื่องการตั้งค่า Web Server หากจะให้บริการหลายๆ Domain อยู่ เพราะต้องใช้ Virtual Server บน nginx ช่วยจัดการให้ เรียกว่า Maintinance ยากพอสมควร แต่ก็ใช้แบบนั้นเรื่อยมาระยะหนึ่งเพราะไม่มีทางเลือก

จนกระทั่งเมื่อไม่นานมานี้ได้พบว่า เราสามารถลง Docker และ Portainer บน Raspberry Pi ได้ สามารถจัดการ Container ผ่านทาง Web GUI ได้ง่ายๆ แค่นั้นล่ะ ไอเดียก็พุ่งกระฉูดและกลายเป็นเหตุแห่งการศึกษาเพิ่มเติมต่อเนื่องเป็นเวลา 3 สัปดาห์ จนประสบความสำเร็จ มีการเอาไปใช้งานจริงบน Production และนี่คือสิ่งที่จะนำมาแบ่งปันกันในบทความนี้นั่นเอง


ทำความรู้จักกับเครื่องมือ

ก่อนจะไปกันต่อ ผมขอแนะนำให้รู้จักเครื่องมือที่นำมาใช้งานกันก่อน

Portainer

https://www.portainer.io

Portainer เป็นเครื่องมือสำหรับช่วยจัดการ Docker Container ข้างในเครื่องของเรา ซึ่งการจะเข้าไป Config Docker ผ่านทาง CLI ความจริงก็ไม่ได้เป็นเป็นปัญหาอะไร แต่เนื่องจาก Option มันเยอะมาก การมี Web GUI คลี่ทุกอย่างออกมาให้ Config ก็เป็นเรื่องดีไม่น้อย แล้ว Portainer ก็ช่วยรองรับส่วนตรงนี้ให้นั่นเอง เป็น Web Interface สำหรับการงาน Docker ให้กับเรา ตั้งแต่ Pull Image ไปจนถึงการ Deploy Container

วิธีติดตั้งนั้นก็แสนง่าย แค่ลง Docker ในเครื่องนั้น แล้วก็รัน Portainer Image ก็เป็นอันสิ้นสุดพิธีการ เพราะตัวมันเองก็เป็น Container เช่นกัน แล้วก็เปิดเข้าผ่านทาง Port 9000 ได้เลย


Cloudflare Tunnel

https://blog.cloudflare.com/cloudflare-argo-tunnel-with-rust-and-raspberry-pi/

ในเมื่อรัน Web Server Container ได้แล้ว แล้วหนึ่งเครื่องของเรามีเว็บตั้งหลายอัน แถมยังตั้งอยู่หลัง Firewall หรือ NAT ด้วย จะจัดการยังไงดี

โชคดีที่พี่ใหญ่ Cloudflare เขาใจดี แบ่งปัน Network ให้ทุกคนใช้ผ่านทาง Service ที่มีชื่อว่า Cloudflare Tunnel

หลักการคือ Cloudflare จะให้เรารัน Service ตัวหนึ่งที่มีชื่อว่า cloudflared เจ้านี่จะทำการเชื่อมต่อเครื่องของเราเข้ากับ Network ของ Cloudflare แบบเดียวกันการทำ Tunnel ของ VPN แต่เป็นจะกลับกัน ก็คือให้ Traffic ภายนอกกลับเข้ามาในเครื่องเรานั่นเอง

จากนั้นก็ทำการเชื่อม Tunnel เข้ากับ Domain ด้วย CNAME Record ทีนี้ก็สามารถเข้าเว็บผ่านทาง Domain Name ได้เลย


Cloudflare Access

https://developers.cloudflare.com/access/about/how-access-works

เครื่องมือตัวสุดท้ายเป็นเรื่องของการอำนวยความสะดวกเรื่อง Security ซึ่งจะไม่ต้องใช้ก็ได้ แต่ผมก็อยากขอแนะนำ

Usecase ในการใช้งาน Stack ด้านบนของผมไม่ได้จบแค่บน Raspberry Pi ที่อยู่บน Local Network ซึ่งถ้าเราเข้า Portainer ผ่านทาง Local Network ก็คงไม่มีใครมี MITM อยู่แล้วล่ะ (มั้ง) แต่ถ้าเราเอา Stack นี้ไปรันบน Public Network ล่ะ แล้วเราเองก็ต้องเข้าไปใช้งานด้วย จะจัดการเรื่อง Security อย่างไร

การไม่เปิดเผย Public IP เป็นหนึ่งใน Concept ที่ดี เพราะจะช่วยให้ Server ของเราปลอดภัยจากการโจมตีโดยตรงผ่านทาง IP เวลา User จะเข้ามาใช้งานก็เข้าผ่านทาง Domai Name เอา ดังนั้นเราเข้าไปทาง http://x.x.x.x:9000 จึงเป็นเรื่องยอมรับไม่ได้

เราจึงทำการส่ง Portainer Dashboard นี้ให้เข้าได้ทาง Cloudflare Tunnel ด้วยเช่นกัน วิธีนี้ช่วยให้สามารถเข้าถึง Portainer จากที่ไหนก็ได้บนโลกผ่านทาง Domain Name ที่เรากำหนด แต่เรื่อง Security ล่ะ ถ้ามีคนรู้ Domain เรา ก็อาจจะโดน Brute Force ได้ใช่ไหมล่ะ

นี่ล่ะคือโจทย์ที่ Cloudflare Access เข้ามาแก้ โดย Cloudflare Access จะทำหน้า Login ให้กับเราผ่านทาง Rules ที่เราเซทได้ผ่านทาง Dashboard เช่น ถ้าเข้ามาทาง Domain ของ Portainer แล้ว ให้ติด Cloudflare Access ส่วนเราจะใช้ Login ด้วยอะไร ก็เลือกได้ จะให้ผ่านทาง Google, Facebook หรืออื่นๆ ก็เลือก เป็นการยกหน้าที่รักษาความปลอดภัยให้กับ Platform ที่เชี่ยวชาญเรื่องนี้อยู่แล้ว มาถึงจุดนี้ก็มั่นใจได้ว่าเฉพาะคนที่ Auth ผ่าน Platform ที่ปลอดภัยมาแล้วเท่านั้นจึงจะเข้าถึง Portainer ของเราได้

ซึ่งมันไม่จบเท่านี้ เพราะว่านี่ไปประยุกต์ใช้กับเรื่องอื่นๆ ได้ด้วย ตัวอย่างเช่น คนทำเว็บ WordPress จะถูกโจมตี wp-login.php กันอยู่เป็นประจำอยู่แล้ว เพราะเว็บ WP มันใช้ Path เดียวกันหมด ดังนั้นวิธีการก็ง่ายมาก เราก็จับ Path wp-login.php จะเข้าได้ต้องต่อผ่าน Cloudflare Access ก่อน เพียงเท่านี้ก็มั่นใจได้เลยว่า จะไม่บอทตัวไหนเข้ามาได้ เพราะว่าติด Google Auth ที่แทบจะเป็นไปไม่ได้ที่จะทะลุเข้ามา และเมื่อบอทมันเห็นว่าเปิด wp-login.php แล้วได้ Response แปลกๆ มันก็จะเลิกโจมตีเว็บเราไปด้วยโดยอัตโนมัติ ลดโหลดไปในตัว

ท้ายที่สุดคือเว็บที่เราทำขึ้นมาใช้กันเอง ถ้าไม่อยากพัฒนา Auth Feature ขึ้นมาใช้เอง ก็สามารถเอาไปซ่อนไว้ข้างหลัง Cloudflare Access ได้เช่นกัน เรียกว่าง่ายสุดๆ และตอบโจทย์เรื่อง Security ได้อย่างสมบูรณ์แบบ


มาตั้ง Stack ที่ว่านี้กัน!

มาถึงเวลาลงมือ ทีนี้แล้วแต่เครื่องแล้วแต่ละคนเลย บางคนอาจใช้ VM ที่มี Docker อยู่แล้วก็ข้ามขั้นตอนที่ 1 แล้วไปต่อที่การติดตั้ง Portainer กันได้เลย แต่สำหรับคนที่มีแต่ Linux ไม่มี Docker เราก็เริ่มจากมาลง Docker กันก่อน

1. ติดตั้ง Docker

ความจริงแล้วนี่เป็นงานที่เกิดขึ้นบ่อยมากเวลาตั้งเครื่องใหม่ เกิดขึ้นบ่อยซะจน Docker มี Script ให้ติดตั้ง Docker ให้ใช้งานได้ง่ายๆ นั่นก็คือ

1curl -sSL https://get.docker.com | sh

รอใส่รหัสผ่าน แล้วก็รอไปยาวๆ ได้เลย

ปรกติจะมีขั้นตอนการทำ Post Docker Installation ซึ่งในกรณีนี้เราไม่จำเป็นต้องใช้ก็ได้ ขอแปะเอาไว้เฉยๆ เผื่อใครสนใจจะไปใช้ในงานอื่นๆ


2. ติดตั้ง Portainer

ความเป็นจริง Portainer ไม่ต้องติดตั้ง แต่เป็นการรัน Portainer บน Docker เสียมากกว่า และแน่นอน คำสั่งก็ง่ายๆ เช่นเดิม

1sudo docker run -d -p 9000:9000 –name=portainer –restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

โดยที่

  • -d บอกว่าให้รันแบบ Daemon หรือรันไว้เป็น Background
  • -p 9000:9000 บอกว่า ให้เปิด Port 9000 ออกมาใช้งาน
  • –name=portainer คือการตั้งชื่อนั่นเอง
  • –restart=always คือการกำหนด Restart Policy เอาไว้ว่า ถ้า Container เกิดปิดไป ให้บูตตัวเองกลับขึ้นมาใหม่เสมอ
  • -v /var/run/docker.sock:/var/run/docker.sock เป็นการ Mount Docker Engine เข้าไปให้ Portainer ใช้ หรือพูดในทางกลับกันก็คือ บอกให้ Portainer ทราบว่าถ้าจะไปใช้งาน Docker ต้องไปใช้งานที่ Path ไหนบนเครื่อง Host
  • -v portainer_data:/data เป็น Path ที่เอาไว้เก็บข้อมูลของ Portaner นั่นเอง ถ้าใครมี Disk หลายลูก อยากจะย้ายข้อมูล Portainer ไปเก็บเอาไว้อีกที่เพื่อความปลอดภัย ก็แก้ Path ที่ตรงนี้เลย โดยถ้าเซทอย่างที่เห็นนี้มันจะสร้าง volume ใหม่ขึ้นมาโดยใช้ของ Doker เอง
  • portainer/portainer-ce:latest อันสุดท้ายก็คือชื่อ Image ที่จะเอามาใช้นั่นเอง ณ ตอนนี้เราใช้เป็น CE หรือ Community Edition ทีนี้ตรงนี้ก็อาจจะมีประเด็นนิดหน่อยสำหรับคนใช้ arm ถ้าไม่สามารถรันได้ อาจจะต้องเลือกให้มัน Pull Image ที่เป็นของ arm มาใช้แทน ซึ่งอันที่ผมโชว์ใช้ได้ทั้ง arm, x86, x64 ดังนั้นยืนยันว่ามีให้งานได้ครับ ใส่ tag ให้ถูกก็ละกัน

จากนั้นก็รันเลย มันก็จะ Pull Image ลงมารัน แปบเดียวก็เสร็จเรียบร้อย เข้าไปหน้า Console ได้เลยผ่านทาง http://{HOST_IP}:9000 ก็จะได้หน้าตาอย่างข้างล่างนี่

สำหรับใครที่ใช้บน Cloud อย่าลืมไปเปิด Firewall tcp:9000 ด้วยนะครับ

ทำการเซท Username / Password ไปตามขั้นตอน แล้วไปหน้าถัดไปเลือก Container Environment เป็น Docker Engine ดังภาพ

เสร็จแล้วก็น่าจะเข้ามาใช้งานได้แล้วล่ะ น่าจะได้เป็นอะไรประมาณในภาพ

พอมาถึงตรงนี้ก็แปลว่า Portainer ของเราพร้อมใช้งานแล้วนั่นเอง


3. รัน Container ผ่าน Portainer

มาถึงตรงนี้เราก็จะมารันโปรแกรมกันแล้ว เพื่อเป็นจุดเริ่มต้นที่ง่าย จะขอรัน nginx ที่เป็น HTTP Server ธรรมดาๆ ให้ขึ้นกันก่อน

วิธีรันโปรแกรมนั้นก็แสนง่าย เข้าไปที่ Containers แล้วกดปุ่ม + Add Container

ตอนสร้าง Container ก็เซทค่านิดเดียว ประมาณในภาพนี้

ในภาพบอกว่า ให้ตั้งชื่อ Container ว่า nginx โดยเลือก Image เป็น nginx ของ Docker Hub

โดยให้เปิด Port มาที่ Host เป็นเลข 8090 ให้เข้าไปที่ Container Port 80 (nginx เปิด Port 80 ไว้เป็น Default)

ความจริงไม่ต้องใช้ 8090 ก็ได้นะครับ จะใช้เลขอะไรก็ได้ตามสะดวก อย่าให้ไปชนกับ Service อื่นๆ ก็พอ แต่จำเอาไว้ด้วยล่ะตั้งเลขอะไรเอาไว้ เพราะจะต้องใช้ต่อในอนาคตครับ

จากนั้นก็กด Deploy the container ได้เลย

รอแปบเดียวทาง Portainer ก็จะพากลับมาหน้าแรก แล้วก็จะเห็น nginx ของเราสร้างขึ้นมาแล้ว พอตามเข้าไปก็จะได้เห็น Container Dashboard ประมาณในภาพนี้

มาถึงจุดนี้ ถ้าเกิดเราเปิดไปที่ http://{HOST_IP}:8090 ก็จะได้เห็นหน้า Welcome to nginx!

ซึ่งจากจุดนี้ ถ้าจะเปิดใช้งานเว็บไซต์ก็ใช้งานได้แล้ว เพียงแต่ชื่อเว็บไซต์สวยๆ ที่ขึ้นต้นด้วย HTTP หรือ HTTPS สวยๆ เท่านั้นที่เราต้องการ และ Cloudflare Tunnel จะเข้ามาช่วยเราต่อจากนี้นั่นเอง


4. ติดตั้ง Cloudflare Tunnel

มาถึงจุดนี้ เราจะพาเว็บไซต์ของเราขึ้นสู่อินเทอร์เน็ตกันแล้ว โดยเครื่องมือที่เราจะใช้กันก็คือ Cloudflare Tunnel นั่นเอง

โดยการจะสร้าง Cloudflare Tunnel นั้น จะประกอบด้วย 5 ขั้นตอนหลักๆ ได้แก่..

4.1 ออกตามหา Cloudflared Image ที่ใช้งานได้

ตัว Official ของ Cloudflare ใช้ cloudflare/cloudflared ปัญหามีอยู่ว่า ตัวนี้รองรับเฉพาะ linux/amd64 ถ้าใครลองดูแล้วใช้ได้ ก็ใช้ตัวนี้ดีสุด

ส่วนใครใช้ Platform อื่นๆ ต้องไปตามหา Image อื่นๆ ซึ่งที่ผมไปค้นดูก็เห็นมี visibilityspots/cloudflared ที่มีคนใช้งานค่อนข้างเยอะ ตัวนี้เป็น cloudflared ตัว dev

ลองเอามาใส่แล้วก็รันดูครับ ซึ่งอันนี้เป็นตัวอย่างหน้าจอที่ผม Config เอามาทดลองรัน

ที่ทำก็คือ ให้มันเปิด cloudflared –version หลังจากที่มันเปิดนั่นเอง

หลังจาก Container สร้างเสร็จแล้ว มันก็จะ Stop ไปอย่างรวดเร็ว เมื่อไปเปิด Log ดู ก็จะเป็นว่ามันจะแสดงเลข Version ของ cloudflared ออกมานั่นเอง

4.2 การ Login เพื่อสร้าง Certificate สำหรับใช้งาน Cloudflare

ต่อมาคือการ Login ให้ Cloudflared เชื่อมกับ Cloudflare Account

การ Login ที่ว่านี้ ถ้าทำสำเร็จมันจะไปเขียนไฟล์ไว้ใน /root/.cloudflared ดังนั้นเราจะต้องทำการ mount volume เอาไว้ด้วย ประมาณในภาพ

ขั้นตอนนี้ถ้ายังไม่มี Volume ให้ใช้ ก็ให้ไปสร้างซะก่อนทางเมนูด้านซ้ายนะครับ แยกออกมาเป็นของ cloudflare โดยเฉพาะ

จากนั้นไปเปลี่ยนคำสั่งที่ใช้รันเป็น login แทน

จากนั้นทำการรัน Container ใหม่ ไปดูที่ Log ก็จะเห็นลิงค์สำหรับ Login ประมาณในภาพ

ให้เอาลิงค์ไปเปิดบน Web Browser จากนั้น Cloudflare ก็จะให้เลือก Domain สำหรับการลิงค์ Account

เมื่อเลือก Domain เรียบร้อยแล้ว มันจะสร้างไฟล์ Cerificate ไว้ให้กับเราไว้ที่ /root/.cloudflared/cert.pem ซึ่งจะตรงกับ Path ที่เรา Mount เอาไว้นั่นเอง ซึ่งก็หมายความว่า หลังจากนี้ที่เรารัน cloudflared ก็จะลิงค์กับ Account นี้เรียบร้อยแล้วนั่นเอง

จากนั้น Container มันก็จะปิดตัวลงไป ก็แปลว่ามันรันงานเรียบร้อยแล้วนั่นเอง

4.3 สร้าง Tunnel ขึ้นมาใช้งาน

กลับเข้าไปที่ Cloudflare เปลี่ยนคำสั่งเป็น tunnel create {TUNNEL_NAME}

โดยตั้งชื่อ {TUNNEL_NAME} เป็นชื่อของเราเอง เช่นในภาพผมใช้เป็นชื่อ subdomain ที่จะใช้เลย (ความจริงตั้งชื่ออะไรก็ได้)

จากนั้นทำการรัน ก็จะได้หน้าตาประมาณในภาพ

ให้จำ Tunnel ID เอาไว้นะครับ จะต้องเก็บเอาไว้ใช้ในขั้นตอนต่อๆ ไป ในภาพคือที่ขึ้นต่อด้วย 034ff… Copy เก็บเอาไว้ได้เลย

4.4 สร้าง Route (Subdomain) สำหรับทำ Tunnel ขึ้นมาใช้งาน

ต่อมาเป็นการสร้าง Subdomain นั่นเอง ใช้คำสั่ง tunnel route dns {TUNNEL_ID} {SUBDOMAIN}

รันเสร็จจะไปดูที่ Log จะได้ประมาณใน Log

มาถึงจุดนี้ให้ลองไปเปิดเว็บ Cloduflare ดู จะเห็น Record CNAME ของ subdomain ที่เราสร้างขึ้นชี้ไปหา Cloudflare Tunnel ก็คือใช้การได้

4.5 รัน Cloudflare Tunnel เพื่อทำการ Route หา Network ในเครื่องของเรา

ทำการเปลี่ยนชื่อ Container ให้สวยๆ เหมาะสมกับงานที่ทำ เพราะนี่จะเป็น Container ที่ไว้รันยาวๆ แล้ว

เปลี่ยนคำสั่งเป็นดังนี้

1<strong>tunnel –url {HOST_URL} run {TUNNEL_ID}</strong>

ไปที่ Network แล้วเซท Network เป็น host ให้มันจะวิ่งผ่านทาง Port ของเครื่อง Host
ความจริงถ้าจะเซทให้ดีๆ ให้สร้าง Network ขึ้นมาใหม่แยกออกมาสำหรับคู่ Container ที่จะ Expose กับ Cloudflare เลย แบบนี้จะช่วยเพิ่มความปลอดภัยได้มากขึ้น เผื่อกรณี App Container โดนเจาะมันก็จะติดอยู่บนเฉพาะวง Network ของมันเอง

หลังจากกดรัน ไปดูที่ Log จะเห็นประมาณในภาพ

ถ้าขึ้นแบบนี้ก็น่าจะใช้ได้ละ เมื่อไปเปิด Subdomain ที่เราสร้างเอาไว้ดู จะได้เห็นว่าเปิดใช้งานได้แล้วลื่นไหลๆ

สังเกตว่า การตั้งค่าทั้งหมดเราไม่มีความจำเป็นจะต้องเข้าไป Shell เองเลย เพียงแค่ต้องรู้ลำดับขั้นตอนที่แน่นอน แล้วก็รันได้เลยยาวๆ ถ้าจับคำสั่งมาเรียงต่อกันดีๆ ก็สามารถรันให้ขึ้นได้เลยตั้งแต่ต้น สำหรับใครที่สนใจก็ลองไปศึกษากันเพิ่มดูนะครับ

อีกเรื่องหนึ่งก็คือ Cloudflare สามารถเขียนไฟล์ Config ได้ เขียนเป็น Ingress ได้ นั่นหมายความว่า Tunnel เดียว สามารถรองรับหลาย Subdomain ได้ ใครสนใจก็ลองไปศึกษากันเพิ่มเตินได้นะครับ

แต่ผมไม่สนใจไปทางนั้นเพราะว่าต้องเข้าไปเขียนไฟล์ใน Shell ผมเลือกเปิดหลาย Container หลาย Tunnel แทน ซึ่งทำให้เราควบคุมแต่ละ Tunnel ได้แยกกันได้ด้วย แต่ข้อเสียก็คือมันจะเปลือง RAM มากกว่า

เรื่องสุดท้ายก่อนจะจบจากตรงนี้ ไปเปลี่ยน Restart Policies ให้เป็น Always ด้วยนะครับ เพื่อที่เวลาเครื่องปิดไปแล้วกลับขึ้นมาใหม่ Container จะได้บูทขึ้นมาพร้อมใช้งานเลย

ความจริงก็จบแล้วสำหรับเรื่องการ Deploy เว็บผ่านทาง Portainer แล้วให้วิ่งออกทาง Cloudflare Tunnel เพียงเท่านี้ก็สามารถให้บริการผู้ใช้ผ่านทางอินเทอร์เน็ตได้ละครับ

ส่วนต่อไปจากนี้คือเรื่อง Security สำหรับใครที่ต้องการจะเปิดสู่ Public แต่ต้องการปกป้องเว็บของเราให้เข้าได้เฉพาะคนที่ต้องการเท่านั้น โดยเครื่องมือที่ผมจะนำมาเสนอก็คือ Cloudflare Access นั่นเอง ลองมาดูกันต่อครับว่าจะเจ๋งเพียงใด


5. Cloudflare Access

มาถึงชิ้นส่วนสุดท้าย Cloudflare Access สำหรับการศึกษารอบนี้ผมก็เพิ่งมารู้จักเครื่องมือนี้เช่นกัน เมื่อได้ลองใช้ดูแล้วจึงพบว่าประทับใจมาก!

หลักการทำงานคร่าวๆ ก็คือ เนื่องจากตอนนี้ Traffic ของเราวิ่งผ่าน Cloudflare Network อยู่แล้ว ถ้าเกิด Cloudflare ทำการเช็ค Auth Header ให้ด้วย มันก็ไม่ใช่เรื่องยากใช่ไหมล่ะ แล้ว Cloudflare ก็ทำแบบนั้นแหละ คือสามารถเซท Rules ได้ว่า ถ้าเข้าผ่านทาง URL นี้แล้วให้ผ่าน Auth ก่อน ปัญหามันอยู่ที่ว่า แล้ว Auth ด้วยวิธีไหนกัน

Cloudflare ก็ทำหน้า Web Interface สำหรับ Auth สวยๆ เลยจ้า ส่วน Auth ของเขาก็เชื่อมต่อกับ Oauth ของเจ้าอื่นๆ เช่น Google, Facebook เหล่านี้ได้ด้วย แล้วการเข้าถึงหน้า Auth ที่ว่านี้อยู่ทางฝั่ง Cloudflare ทั้งหมด ยังมาไม่ถึง Origin Server นี่จึงเป็น Service สำหรับการกั้นการเข้าถึง Resource ที่ปลอดภัยมากๆ

หน้าที่เห็นอยู่ข้างบน เป็นตัวอย่างหน้าที่ขึ้นมากั้นก่อนเราจะเข้าถึง Tunnel นั่นเอง ซึ่งตัวอย่างข้างบนผมให้มันกันการเข้าถึง wp-login.php ของ WordPress เพราะ Path นี้โดนโจมตีประจำ

ทีนี้มันมาเกี่ยวอะไรกับ Project ที่เรากำลังทำกันอยู่?

จินตนาการว่า เราอยากจะเข้าถึง Portainer จากที่ไหนก็ได้ แต่ถ้าเราต้อง Expose หน้า Login ของ Portainer ขึ้นอินเทอร์เน็ตก็คงมีความเสี่ยง ถึงแม้จะไม่มีคนรู้ Password แต่ก็อาจจะโดน Brute-force Attack ได้อยู่ดี เราจึงเอา Cloudflare Access มากั้นข้างหน้า คือต้องผ่านด่านนี้ไปให้ได้ก่อน ถึงจะเข้าไป Login ได้ ซึ่งด่านนี้ที่ผมทำ ผมเอาไปเชื่อมกับ Google SSO ก็เหมือนกับว่า ต้องผ่านด่าน Google Auth ไปก่อนเลยก็ไม่ผิดนัก ติด 2FA ของ Google เข้าไปอีก มาถึงจุดนี้ผมว่าถ้าทะลุเข้ามาได้อีกก็เทพเจ้าแล้ว

เอาล่ะ ทีนี้จะมาเปิดใช้งานทำยังไง

เข้าไปที่ Cloudflare Dashboard จะเห็นปุ่ม Access ด้านบน กดเข้าไปเลย แล้วกดเข้าไปที่ Launch Teams

กดครั้งแรกมันจะต้อง Setup หน่อย ก็เซทไปตามขั้นตอนที่นี่ Setting up Cloudflare for Teams

จากนั้นไปที่ Access > Applications แล้วกด Add an application

เลือกเป็น Self-hosted

ทำการตั้งค่าประมาณในตัวอย่าง

เลือกวิธีการเข้าถึงที่ต้องการ ซึ่งของผมมี Google ให้ใช้เพราะว่าไปเชื่อมกับ Google OAuth เอาไว้แล้ว ถ้าเกิดใครอยากใช้บ้างก็ลองไปอ่าน Documentation ดู ตอนแรกจะใช้ One-time PIN ไปก่อนก็ได้ อันนี้มันจะส่งเลขไปให้กรอกทางอีเมล์ ก็ถ้าว่าไม่แย่เหมือนกัน

ทำการเซท Rules โดยใส่อีเมล์ที่ต้องการให้เข้าถึง Resource ดังกล่าวได้

เรียบร้อยแล้วก็ Next แล้วกด Add Application ได้เลย

พอเข้าหน้าเว็บที่เซท Rules เอาไว้ ก็จะติด ต้อง Login ให้ผ่านทางฝั่ง Cloudfalre Access ก่อน

ทีนี้ก็ลองไปสร้าง Cloudflare Tunnel สำหรับ Portainer:9000 ให้มันชี้ออกมาที่สัก subdomain หนึ่ง เราจะสามารถเข้าถึงได้จากทุกที่แล้ว จากนั้นติด Cloudflare Access เข้าไปข้างหน้า เพียงเท่านี้ก็สามารถเปิด Portainer สู่อินเตอร์เน็ตแต่ยังสบายใจได้ว่ามันปลอดภัยแล้วล่ะ

โยนงาน Security ยากๆ ไปให้ทางฝั่ง Cloudflare ทางฝั่ง Google เป็นคนจัดการซะ

ปล. Cloudflare Tunnel สามารถใช้กับ SSH ได้ด้วยนะ แล้ว Cloudflare Access ก็มี Auditable Terminal ทำให้สามารถ Access SSH ได้ผ่านทางหน้าเว็บเลย เรียกว่าล้ำสุดๆ เอาไว้จะมาเล่าเรื่องนี้ให้ฟังกันอีกครั้ง


แนะนำการตั้งค่า Security เพิ่มเติมสำหรับผู้จะเอาไปใช้งานจริงจัง

แน่นอนที่ผมเสนอไปข้างบนนั้นก็ทำให้เรามี Service ขึ้นมาใช้งานได้แล้ว แต่มันเป็นการเซทแบบหลวมๆ ขอแค่ให้ใช้ได้ แต่ถ้าจะนำไปใช้งานจริง เราควรคำนึงถึงจังหวะที่ App ของเราอาจจะมีช่องโหว่ Hacker สามารถทะลุเข้ามาถึงไฟล์หรือ Network บน Container และจุดนี้ล่ะที่เราจะมาดูกันเพิ่ม ซึ่งผมขอให้ไว้เป็น Guideline เผื่อใครจะเอาไปใช้งานจริง

  1. การรัน Container ไม่ควร Run ด้วย Root User กรณี Container ถูกแฮค จะได้ไม่โดนแก้ไข File System ต่างๆ (เซท chmod/chown เอาไว้ให้เหมาะสมด้วย อย่าใช้ 777 ล่ะ)
  2. จังหวะเชื่อม Container 2 ตัวเข้าด้วยกัน ระหว่าง App กับ Tunnel ให้สร้าง Network แยกออกมาจากเฉพาะของมัน 2 ตัวนี้เลย แล้วให้ Cloudflared ชี้ไปหา http://{CONTAINER_NAME}:80 ก็จะใช้งานได้ โดยที่ไม่จำเป็นต้อง Expose App Network ออกมาเลย
  3. ดูแลไฟล์ cert.pem ของ Cloudflared ให้ดี ไฟล์นี้สามารถเข้าถึงบริการของ Cloudflare ได้ เช่น สร้าง หรือ ลบ DNS Records อาจทำให้เว็บล่ม สร้างความหายนะครั้งใหญ่ได้ถ้าหลุดออกไป
  4. ไฟล์ cert.pem ใช้แค่ตอนสร้าง Tunnel ใหม่ การสร้าง DNS Record ใหม่ ไม่ได้เกี่ยวอะไรกับการรัน Tunnel ดังนั้นความจริงถ้าใช้งานเสร็จแล้ว ลบทิ้งไปเลยก็ได้ จะสร้าง Tunnel เพิ่มเมื่อไหร่ค่อยมา Login ใหม่อีกครั้งก็ได้
  5. ไฟล์ {TUNNEL_ID}.json เป็นไฟล์ที่ใช้สำหรับการรัน Tunnel ความจริงสามารถ Generate แล้วค่อยไปวางไว้บนเครื่องปลายทางก็ได้ เพื่อรักษาความปลอดภัยของไฟล์ cert.pem
  6. เวลารัน Tunnel ความจริง Mount ไฟล์ {TUNNEL_ID}.json เข้าไปไฟล์เดียวก็พอ แล้วก็เซทเป็น Read Only เอาไว้ด้วย

สำหรับบทความแนะนำ Portainer, Cloudflare Tunnel, Cloudflare Access ก็จบที่เท่านี้แล้วครับ หวังว่าจะได้เห็นแนวทางใหม่ๆ พาให้เกิดไอเดียว่าจะนำไปใช้ประโยชน์กันอย่างไรได้บ้าง

สำหรับผมตอนนี้ก็มี Project ที่เอา Stack นี้ไป Deploy บน Production แล้วจริงๆ ด้วยนะครับ เนื่องจากเป็น Project ค่อนข้างเล็กแต่สำคัญต้อง Maintain เอาไว้ แต่ลูกค้าก็ดันเข้ามาใช้งานน้อยเหลือเกิน ก่อนหน้านี้เอาไป Deploy บน Kuberenetes Cluster ก็เลยไม่คุ้ม สุดท้ายลดลงมาเหลือ VM ตัวเดียวแล้วให้ Portainer ช่วย Manage เรียกว่าสบายไปเลยแล้วก็ราคาถูกด้วย จากนั้นก็ Backup VM Disk เอาไว้เผื่อโชคร้ายจะได้ Restore กลับมาได้ในวันที่มีปัญหาจริงๆ

ส่วนวันนี้ก็ขอจบที่ตรงนี้ละกันครับ แล้วครั้งหน้าจะมีแนะนำการใช้Cloudflare Tunnel และ Cloudflare Access เพิ่มเติม รับรองว่าถูกใจสาย DIY Home Server แน่นอน

ถ้าใครคำถามหรือข้อติชมก็ฝากกันไว้ได้ที่กล่อง Comment ข้างใต้โพสได้เลยนะครับ
แล้วพบกันใหม่โอกาสหน้า
สวัสดีครับ

ที่มา: https://www.spicydog.org/blog/setup-web-server-on-portainer-and-cloudflare-tunnel/

ใส่ความเห็น