LUNARTHALE

ติดตั้ง mosquitto ใน jail บน FreeBSD ด้วย BastilleBSD

26-March-2020


Mosquitto เป็น หนึ่งใน opensource mqtt service broker ที่นิยมใช้กันสำหรับการส่งข้อความไปมาระหว่างอุปกรณ์ต่างๆในวงเครือข่ายของ IoT โดยที่ Mosquitto เป็นโครงการที่ถูกดูแลโดยมูลนิธิ eclipse ที่มีชื่อเสียงจากการทำ ide ของ java ที่ผมเชื่อว่าหลายๆคนก็เคยใช้กันครับ คราวนี้แล้วอะไรคือ jail อะไรคือ FreeBSD และ อะไรคือ BastilleBSD มาลองดูกันครับ

This article is using FreeBSD 12.1 & Bastille 0.6.0

อรัมภบท

ก่อนอื่นเลยในยุค 2020 คนส่วนใหญ่เวลาจะลง service อะไรมักก็จะนึกถึงพวก container ต่างๆ เช่น docker ที่เป็นที่นิยมทั่วไป หรือ podman ของ RedHat ที่เป็น rootless container อันไหนดีกว่า ผมก็ยังไม่ค่อยจะแน่ใจเท่าไรแต่ตั้งใจไว้ว่าว่างๆจะหาเวลามาศึกษาดูครับ แต่สิ่งนึงที่ทั้งสองอันนี้เหมือนกันก็คือมันเป็น linux native ทั้งหมดครับ

อย่างที่รู้ๆกันว่าระบบปฏิบัติการในปัจจุบันนั้นมีหลากหลายมาก ระบบปฏิบัติงานนึงที่ค่อนข้างมีชื่อเสียงนั่นก็คือ FreeBSD ตัว FreeBSD เองนั้นก็สามารถที่จะลง docker ได้นะครับ แต่จำเป็นที่จะต้องมี linux layer หรือ packages มาลงด้วยไม่ได้เป็น native แบบอย่างแท้จริง สำหรับ FreeBSD แล้วเขามีอุปกรณ์ตัวนึงที่ชื่อว่า jail หรือคุกที่จะไว้กักขัง กักตัว หน่วงเนี่ยว process ไม่ให้ไปไหนหรือออกนอกพื้นที่ที่ถูกตั้งไว้ไปได้ ฟังดูแล้วก็คล้ายกับ container อยู่ใช่ไหมครับ

Jail and FreeBSD

FreeBSD 12.1 นี้มีอยู่บนผู้ให้บริการ(VPS) หลากหลายรายเช่น UpCloud, DigitalOcean, Vultr และก็อื่นๆอีกหลายหลายเจ้าครับ (ในบทความนี้ผมทำบน VM ของผมนะครับ) สำหรับตัว Jail นั้นถ้ามีฝีมือคุณก็สามารถตั้งค่าได้เองเลยเพียงแค่เข้าไปอ่านบน FreeBSD Handbook ก็เท่านั้นเอง แต่ถ้าอยากสะดวกสบายหน่อยก็จะมีพวกตัวช่วยอยู่เช่น

แต่ละอันก็ถูกเขียนจากภาษาที่แตกต่างกัน และมีความสามารถที่แตกต่างกันออกไป ตัว Bastille ที่ผมใช้นะเขาบอกว่าจุดเด่นคือใช้งานง่ายและขนาดเล็ก(สารภาพก่อนเลยว่าผมเลือกตัวนี้ผม website เขาสวยและ docs อ่านง่ายมากๆๆ)

ติดตั้ง bastille : jail manager

ข้อมูลส่วนใหญ่ออกแนวอ้างอิงมาจากวิธีการติดตั้งบนเว็บไซต์หลักของเขาครับ วิธีการติดตั้ง

มาเริ่มกันเลยหลังจากที่เราสร้าง droplet เสร็จก็จะเจอหน้าตาแบบเปล่าสิ่งที่ต้องทำก่อนเสมอหลังจากติดตั้ง FreeBSD เสร็จก็คือการอัพเดท patch และ ติดตั้ง patch ซึ่งเราสามารถทำได้ด้วยวิธีการนี้

root@App02:~ # freebsd-update fetch
root@App02:~ # freebsd-update install

คำสั่งข้างบนก็ออกแนวคล้ายๆกับ "apt update & apt upgrade" นั่นแหละครับ คราวนี้เราก็พร้อมที่จะลง packages แหละ แต่เดียวก่อน???? เนื่องจาก FreeBSD มีการอัพเดท package แบบ quaterly หรือทุกๆ 3 เดือน ดังนั้นเพื่อให้เราได้ packages ที่ใหม่ๆสดๆจากต้นน้ำเราจึงจำเป็นต้องทำการเลือกของเราให้เป็นสายต้นน้ำก่อน "ไม่จำเป็นต้องทำถ้าโอเคหรือเน้นความเสถียรเป็นหลัก"

root@App02:~ : mkdir -p /usr/local/etc/pkg/repos
root@App02:~ : echo 'FreeBSD: { url: 'pkg+http://pkg.FreeBSD.org/\$\{ABI\}/latest', enabled: yes }' > /usr/local/etc/pkg/repos/FreeBSD.conf

และเมื่อเราพร้อมก็ถึงเวลาติดตั้ง bastille กันครับ คำสั่งก็จะคล้ายกับฝั่ง linux เลย :)

root@App02:~ : pkg install bastille

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

root@App02:~ : sysrc bastille_enable="YES"
bastille_enable:  -> YES

ตั้งค่า bastille

หลังจากเราติดตั้ง bastille เสร็จแล้ว เราก็จำเป็นที่จะต้องทำการสร้าง network interface ให้กับ jail ของเรา ตรงนี้จะต่างจาก default ของ docker นิดนึงตรงที่เราจำเป็นต้องสร้างทุกอย่างขึ้นมาเองทั้งหมดก่อน โดยที่ไฟล์ config ของ bastille จะอยู่ที่ /usr/local/etc/bastille/bastille.conf โดยปกติแล้วไฟล์ config ที่ user ทำการติดตั้งเองจะถูกเก็บที่ /usr/local/etc/ ในขณะที่ถ้าเป็นของ system จะเก็บที่ /etc/ การเก็บแบบนี้ทำให้หาง่ายขึ้นเยอะๆมากๆครับ

ตั้งค่า local network

เพื่อให้ jail ของเรามี net ใช้ได้ได้ เราจึงจำเป็นต้องทำการสร้าง interface ขึ้นมาก่อนวิธีการทำ interface ก็คือ

root@App02:~ : sysrc cloned_interfaces+=lo1
root@App02:~ : sysrc ifconfig_lo1_name="bastille0"
root@App02:~ : service netif cloneup

เราได้ทำการสร้าง network ภายในที่ชื่อว่า "bastille0" เป็นที่เรียบร้อยแหละ ก็เหลือแค่ทำการสร้าง NAT เพื่อให้ jail ของเราสามารถใช้ได้ โดยเราจะใช้ built-in firewall ของ FreeBSD ที่ชื่อ PF (Packet Filter)

This was directly copy from bastille guideline

ext_if="em0" ## ***

set block-policy return
scrub in on $ext_if all fragment reassemble
set skip on lo

table <jails> persist
nat on $ext_if from <jails> to any -> ($ext_if)

## static rdr example
## rdr pass inet proto tcp from any to any port {80, 443} -> 10.17.89.45

## dynamic rdr anchor (see below)
rdr-anchor "rdr/*"

block in all
pass out quick modulate state
antispoof for $ext_if inet
pass in inet proto tcp from any to any port ssh flags S/SA modulate state

# If you are using dynamic rdr also need to ensure that the external port
# range you are using is open

*** ตรงนี้ให้เปลี่ยนเป็น interface ที่ต่อเน็ตข้างนอกได้นะครับให้เช็คจาก ifconfig

ทำการ enable firewall เพื่อเสร็จสมบูรณ์

root@App02:~ : sysrc pf_enable="YES"
root@App02:~ : service pf start

สร้าง Jail

ตอนนี้เราก็ทำทุกอย่างพร้อมแล้ว มาเริ่มสร้าง jail กันดีกว่า เริ่มด้วยการโหลด image ก็เหมือนคำสั่งในการ pull image ลงมาของ docker นั่นแหละครับ ดึง image ลงมาเพื่อเอาไว้เป็นฐานในการสร้าง jail แต่ละ jail

root@App02:~ : bastille bootstrap 12.1-RELEASE update

ทำการสร้าง jail ขึ้นมาในที่นี้ผมจะให้ jail ของผมชื่อว่า "mosquitto-mqtt" โดยที่คุณต้องกำหนด ip address ของ jail ตัวนี้เองครับ ให้มันอยู่ใน range ของ 3 ตัวนี้ครับ 10.0.0.0/8, 172.16.0.0/12 และ 192.168.0.0/16

root@App02:~ : bastille create mosquitto-mqtt 12.1-RELEASE 10.17.89.100

start jail ขึ้นมา

root@App02:~ : bastille start mosquitto-mqtt

ติดตั้งและตั้งค่า mosquitto

ก่อนอื่นเลยเราก็ต้องเข้าคุกเข้าตารางก่อนด้วยคำสั่ง เพื่อเข้าไปใน jail ของเราที่สร้างขึ้นไว้

root@App02:~ : bastille console mosquitto-mqtt

ทีนี้เราก็จะเข้ามาอยู่ในคุกที่เราสร้างไว้ เราก็พร้อมที่จะเข้าสู่อาหารจานหลักนั่นก็คือติดตั้ง mosquitto กันซักที

root@mosquitto-mqtt:~ : pkg install mosquitto

เหมือนเช่นเคยครับ ลงเสร็จเราก็ต้องทำการ enable service ด้วยด้วยคำสั่ง sysrc mosquitto_enable="YES" ผมชอบจำเป็น true อยู่เรื่อยเลยแหะ ก็จะเสร็จกับการติดตั้ง

เวทอะมินิด เรายังต้องทำการตั้งค่าอีกซักนิดๆหน่อยก่อนที่เราจะ start mosquitto ขึ้นมาได้ เช่นเคยครับ config ของ mosquitto ก็จะอยู่ที่ /usr/local/etc/mosquitto/mosquitto.conf ตัวไฟล์จะมีคำอธิบายอย่างละเอียดว่าแต่ละตัวแปรหมายความว่าอะไรให้ด้วย อย่างยาวเลยแหละ!! แต่คราวๆที่จำเป็นก็คือต้องเป็น user nobody ไม่ใช่ค่า default user mosquitto หลักจากนั้น mosquitto ก็พร้อม start แล้ว

root@mosquitto-mqtt:~ : service mosquitto start

ยังไม่เสร็จนะ เราแค่ทำการลง mosquitto เสร็จเฉยๆ เรายังต้องทำการ port binding กับ host ด้วยไม่งั้นคนนอกก็จะไม่สามารถใช้ service ของเราได้

ตั้งค่า pf อีกรอบ

กลับมาอยู่บน host อีกรอบในรอบนี้เราต้องทำการ bind port ของ host เข้ากับของ jail ซึ่งก็สามารถทำได้โดยใช้คำสั่ง rdr ของ bastille

root@App02:~ : bastille rdr mosquitto-mqtt tcp 1883 1883

default port ของ mosquitto เป็น 1883 แต่เป็น unecrypted channel ถ้าต้องการ encrypted ก็ต้องใช้ port 8883 และมี cert SSL ผมขอติดไว้ครั้งหน้านะ

นอกจากที่จะทำการ bind port ไปแล้วเราก็ยังต้องทำการแก้ไขไฟล์ /etc/pf.conf/ ของเราด้วย ไม่งั้นก็จะไม่สามารถใช้ port นี้ได้ โดยให้แก้เป็น

pass in inet proto tcp from any to any port {1883, ssh} flags S/SA keep state

ที่เหลือก็แค่ restart pf เพื่อให้ตั้งค่าใหม่อัพเดท

root@App02:~ : pfctl -f /etc/pf.conf

เพียงเท่านี้ ก็เสร็จหมดทุกอย่างแล้ว เฮ้!

ทดสอบทุกอย่าง

เมื่อเราลงทุกอย่างเสร็จหมดอันเป็นที่เรียบร้อยก็ถึงเวลาทดสอบกันซักที เราจะทดสอบด้วยคำสั่ง mosquitto_pub ที่จะรันจากอีกเครื่อง

root@App03:~ : mosquitto_pub -h app02.local -t "vm/shoutout" -m "Stay at home is so boring" -d
Client mosq-X2wcbXbfu9nsUQo43Z sending CONNECT
Client mosq-X2wcbXbfu9nsUQo43Z received CONNACK (0)
Client mosq-X2wcbXbfu9nsUQo43Z sending PUBLISH (d0, q0, r0, m1, 'vm/shoutout', ... (25 bytes))
Client mosq-X2wcbXbfu9nsUQo43Z sending DISCONNECT

ส่งได้เรียบร้อยสบายใจ

ส่งท้าย

จะเห็นได้ว่าการที่จะติดตั้ง mosquitto ใน jail ของ freebsd นั้นเราอาจจะต้องทำ manually มากกว่า docker อยู่หลายขุมทีเดียวเชียวละ แต่ก็แลกด้วยความปลอดภัยและความโปร่งที่มาเป็นการตั้งค่าพื้นฐาน ก็อย่างที่บอกว่าปีนี้ผมตั้งใจจะลองเล่น FreeBSD และ OpenBSD ดู ถ้าใครสนใจก็ลองมาคอยอ่านได้นะครับ แต่ผมว่าไม่น่าจะมีใครอ่านมากกว่านั้น authlog แทบไม่เด้งเลยฮา มีแต่บอททั้งนั้น T_T

P.S. Take care of yourself during this tough time, and I wish you all the best.


For questions and comments, please send it to my public mailing list. Thank you.