แชร์โฟลเดอร์ torrent ด้วย Samba: force user และกับดัก bind interfaces
แชร์โฟลเดอร์ข้าม Windows กับ Linux ง่ายตอนเปิดครั้งแรก แต่เจ็บตอนที่ไฟล์สองฝั่งมีเจ้าของคนละคนแล้วลบของกันไม่ได้ สูตรสั้น ๆ ที่แก้ได้คือบังคับให้ทุกการเขียนผ่าน Samba กลายเป็น user เดียวกับ daemon
สรุปใน 1 นาที
- อาการ/ปัญหา
- แชร์โฟลเดอร์ดาวน์โหลดข้าม Windows กับ Linux แล้วเจ้าของไฟล์คนละคน ลบของกันไม่ได้
- ใครควรอ่าน
- คนที่ตั้ง Samba บน Debian ให้เปิดโฟลเดอร์ของ Transmission จาก Windows
- ทางแก้แบบสั้น
- ใช้ force user, force group และ create mask ให้เขียนเป็น user เดียวกับ daemon และเช็กบรรทัด bind interfaces only ที่ถูกคอมเมนต์อยู่
พอดีผมมีเซิร์ฟเวอร์ Debian ตัวหนึ่งรัน transmission-daemon ดาวน์โหลดลงดิสก์แยกที่ mount ไว้ แล้วอยากเปิดโฟลเดอร์นั้นให้ Windows ในบ้านเห็นเป็นไดรฟ์เครือข่าย เปิดดู แก้ชื่อ และลบไฟล์ได้ โดยที่ Transmission ยังจัดการไฟล์เดียวกันต่อได้ ถ้าตั้ง Samba แบบพื้นฐาน ไฟล์ที่ฝั่ง Windows สร้างกับไฟล์ที่ Transmission สร้างจะมีเจ้าของคนละคน แล้วต่างฝ่ายต่างลบหรือย้ายของอีกฝั่งไม่ได้ ผมเลยเขียนสูตรที่ใช้อยู่ไว้ตรงนี้ สั้นกว่าบทความอื่น ๆ เพราะเป็นสูตรปฏิบัติ
ชื่อโฟลเดอร์ ชื่อ share ชื่อ interface และ IP ในบทความเป็นตัวอย่างทั้งหมด (/srv/downloads, Downloads, br0, 192.168.10.52) ส่วนเรื่องตำแหน่งที่ติดตั้ง ถ้าเครื่องเป็นโฮสต์ของ VM ในงานจริงควรแยก Samba ไปไว้ใน VM หรือ LXC ไม่ควรติดตั้งบนตัว hypervisor นี่เป็นความเห็นทั่วไป ไม่ใช่สิ่งที่ผมทำในเคสนี้
ทำไมต้อง force user
ปัญหาคือ Samba ใช้ user ของคนที่ล็อกอินเป็นเจ้าของไฟล์ที่สร้าง สมมติผมล็อกอินด้วย user ชื่อ media ไฟล์ที่ Windows เขียนลงไปจะเป็น media:media ส่วนไฟล์ที่ Transmission ดาวน์โหลดเป็นของ debian-transmission พอ Transmission จะลบหรือย้ายไฟล์ของ media ที่ผมเพิ่งเปลี่ยนชื่อ มันทำไม่ได้ และทางกลับกัน Windows ก็แก้ไฟล์ของ daemon ไม่ได้เหมือนกัน
ทางแก้คือ force user กับ force group ที่ทำให้ทุกอย่างที่เข้ามาทาง share นี้ถูกมองเป็น debian-transmission ทั้งหมดในระดับระบบไฟล์ และใช้ create mask กับ directory mask กำหนดสิทธิ์ของไฟล์ใหม่ให้ทั้ง user และ group เขียนได้

config ที่ใช้
เริ่มจากสำรองไฟล์เดิมก่อนแก้ทุกครั้ง เพราะ Samba เป็นของที่แก้ผิดแล้วต้องหาทางกลับ
sudo cp -a /etc/samba/smb.conf /etc/samba/smb.conf.orig-$(date +%Y%m%d)
# user สำหรับล็อกอิน share (ไม่มี shell ไม่มี home ใช้ได้แค่ผ่าน Samba)
sudo useradd -M -s /usr/sbin/nologin media
sudo smbpasswd -a media # ตั้งรหัสของ Samba แยกจากรหัส Linux
[global]
interfaces = lo br0
bind interfaces only = yes
server min protocol = SMB2
map to guest = never
# ...ค่า global อื่นที่มากับ Debian...
[Downloads]
path = /srv/downloads
valid users = media
guest ok = no
read only = no
force user = debian-transmission
force group = debian-transmission
create mask = 0664
directory mask = 0775
อธิบายทีละกลุ่ม interfaces กับ bind interfaces only ให้ Samba ฟังเฉพาะ loopback กับ NIC ของ LAN ผมวางสองบรรทัดนี้ทันทีหลังบรรทัด [global] ซึ่งเรื่องนี้มีกับดักอยู่ ขอเล่าในหัวข้อถัดไป ส่วน server min protocol = SMB2 ปิดโปรโตคอลเก่า map to guest = never กับ guest ok = no ไม่เปิด guest เลย ต้องล็อกอินด้วย media เท่านั้น
ใน share ผมให้ valid users = media คือมีคนเดียวที่ล็อกอินได้ แต่ทุกการเขียนไฟล์ถูกบังคับให้เป็น debian-transmission ผ่าน force user กับ force group ส่วน create mask = 0664 กับ directory mask = 0775 ทำให้ไฟล์ใหม่ที่ Windows สร้างมีสิทธิ์ที่ทั้ง user และ group เขียนได้ หลังแก้ไฟล์ ตรวจ syntax แล้วค่อย restart
testparm # ต้องไม่มี error และโชว์ Loaded services file OK
sudo systemctl restart smbd
ผมยังไม่ได้ตั้งหรือตรวจว่า transmission-daemon ใช้ umask แบบไหนตอนสร้างไฟล์ดาวน์โหลดเอง ถ้าอยากให้ไฟล์ที่ Transmission สร้างตรงกับ 0664 และ 0775 ทั้งหมด ต้องไปดูฝั่ง daemon ต่างหาก ในเคสนี้ผมพึ่งแค่ว่าเจ้าของไฟล์ทั้งสองฝั่งเป็น user เดียวกัน ซึ่ง force user ทำให้แล้ว
ฝั่ง Windows: map เป็นไดรฟ์ถาวร
ฝั่ง Windows เก็บ credential ด้วย cmdkey แล้ว map เป็นไดรฟ์ตัวอักษรเดียว ใส่ /persistent:yes ให้กลับมาหลังรีบูต ตัวอย่างนี้ใส่ <secret> แทนรหัสจริง
cmdkey /add:192.168.10.52 /user:media /pass:<secret>
net use Z: \\192.168.10.52\Downloads /persistent:yes
dir Z:\
ถ้าไฟล์ถูกสร้างจาก Windows แล้วไปดูบนเซิร์ฟเวอร์ ควรเห็นเจ้าของเป็น debian-transmission ตามที่ force user บังคับ ไม่ใช่ media
ls -l /srv/downloads | head
# -rw-rw-r-- 1 debian-transmission debian-transmission ... ไฟล์ที่สร้างจาก Windows
กับดักที่หนึ่ง: bind interfaces only ที่ถูกคอมเมนต์ไว้
เครื่องมี NIC หลายตัว ตัวหนึ่งต่อ LAN อีกตัวเป็นช่องจัดการ หรือช่องต่อโมเด็ม ผมไม่อยากให้ Samba ฟังที่ตัวอื่นนอกจาก LAN เลยตั้ง interfaces กับ bind interfaces only และมีเงื่อนไขว่าอย่าให้กลายเป็นช่องโหว่ ถ้าเครื่องมี IP สาธารณะหรือ NIC ที่ต่ออินเทอร์เน็ตตรง ๆ ควรปิดช่องทางนั้นด้วย firewall เพิ่มอีกชั้น ซึ่งเป็นหลักทั่วไป
กับดักที่ผมเจอจริงคือ ไฟล์ smb.conf ค่าเริ่มต้นของ Debian มีบรรทัดแบบนี้อยู่แล้ว
; bind interfaces only = yes
บรรทัดนั้นถูกคอมเมนต์ด้วยเครื่องหมาย ; ผมมีสคริปต์ติดตั้งที่เช็กก่อนเพิ่มค่าว่ามีบรรทัดนี้อยู่แล้วหรือยัง ด้วย grep แบบไม่ยึดต้นบรรทัด เจอบรรทัดที่คอมเมนต์ก็นึกว่ามีแล้ว เลยข้ามการเพิ่ม ผลคือ Samba ไม่ได้ bind จริง และไม่มี error ใด ๆ บอก
วิธีแก้มีสองส่วน ตรวจด้วย pattern ที่ยึดต้นบรรทัดและไม่นับบรรทัดคอมเมนต์ และตรวจผลจริงที่ Samba อ่านได้ ไม่ใช่ดูแค่ไฟล์
# ผิด: เจอบรรทัดที่ถูกคอมเมนต์ด้วย เลยนึกว่ามีแล้ว
grep -q 'bind interfaces only' /etc/samba/smb.conf && echo "มีแล้ว (หลอก)"
# ถูก: นับเฉพาะบรรทัดที่ไม่ใช่คอมเมนต์
grep -Eq '^[[:space:]]*bind interfaces only' /etc/samba/smb.conf && echo "มีจริง"
# พิสูจน์ด้วยค่าที่ Samba ใช้จริง (รันหลัง restart)
testparm -s 2>/dev/null | grep -iE 'bind interfaces|interfaces ='
# พิสูจน์ว่า smbd ฟังที่ไหนบ้าง
sudo ss -ltnp | grep smbd
บรรทัดสุดท้ายคือด่านที่ผมไว้ใจที่สุด เพราะมันดูสิ่งที่ระบบเปิดฟังอยู่จริง ถ้า smbd ฟังที่ 0.0.0.0:445 แสดงว่ายังไม่ได้ bind ถ้าเห็นเฉพาะ IP ของ LAN กับ loopback ถึงถือว่าถูกต้อง ขั้น ss นี้เป็นวิธีตรวจทั่วไปที่ผมแนะนำให้ทดสอบบนเครื่องตัวเองด้วย ส่วนการวางบรรทัดใหม่ทันทีหลัง [global] ผมทำเพื่อไม่ให้ไปปนกับบรรทัดคอมเมนต์เดิมที่อยู่ลึกลงไป

ค่าตัวอย่างที่คอมเมนต์ไว้ในไฟล์ค่าเริ่มต้นหลอก grep ได้ทุกครั้งที่ pattern ไม่ยึดต้นบรรทัด ทุกครั้งที่สคริปต์ติดตั้งจะ "เพิ่มถ้ายังไม่มี" ให้ตรวจผลที่ testparm -s หรือ ss ควบคู่ ไม่ใช่เชื่อแค่ผลของ grep บนไฟล์
ตัวเลข mask อ่านยังไง และ force user ทำให้เสียอะไร
ตัวเลข 0664 กับ 0775 คือสิทธิ์ของ Linux สามชั้นเรียงตามเจ้าของ กลุ่ม และคนอื่น โดย 6 คืออ่านกับเขียน 7 คืออ่าน เขียน และรันหรือเข้าโฟลเดอร์ 4 คืออ่านอย่างเดียว ดังนั้นไฟล์ใหม่เป็น rw-rw-r-- ส่วนโฟลเดอร์ใหม่เป็น rwxrwxr-x เหตุที่โฟลเดอร์ต้องมี x เพราะไม่มี x ก็เข้าโฟลเดอร์ไม่ได้ จุดสำคัญคือทั้งเจ้าของและกลุ่มเขียนได้ ถึงแม้ใครจะเปลี่ยนมาเป็น user อื่นในกลุ่มเดียวกันทีหลังก็ยังแก้ไฟล์ได้ ส่วนคนอื่นนอกกลุ่มอ่านได้อย่างเดียว
ราคาที่ต้องจ่ายของ force user มีสองข้อที่ควรรู้ก่อนเลือกใช้ ข้อแรก ทุกการเขียนผ่าน share นี้ถูกมองเป็น debian-transmission ในระดับระบบไฟล์ ถ้าอยากรู้ว่าใครสร้างหรือลบไฟล์ ต้องดูจาก log ของ Samba เอง เพราะระบบไฟล์ไม่เก็บชื่อคนที่ล็อกอินไว้ ข้อสอง ใครก็ตามที่อยู่ใน valid users จะมีสิทธิ์เท่ากับ daemon เสมอ ตอนนี้ผมให้มี user เดียวคือ media เลยไม่เป็นปัญหา แต่ถ้าวันหนึ่งเพิ่มคนเข้าไปหลายคน และอยากแยกสิทธิ์ใครอ่านอย่างเดียว ก็ต้องทำ share แยกอีกตัวที่ตั้ง read only = yes ไม่ใช่เอาไปปนในตัวนี้
ทางเลือกอื่นที่ผมรู้คือใช้ POSIX ACL กำหนดสิทธิ์ราย user บนโฟลเดอร์ หรือไล่ chown ตามหลังเป็นระยะ ซึ่งผมไม่ได้เลือก เพราะ ACL ซับซ้อนขึ้นอีกชั้นสำหรับโฟลเดอร์ที่ใช้งานคนเดียว และ chown ตามหลังต้องมีใครสักคนจำว่าต้องรัน force user ตัดปัญหานี้ที่ต้นทางและตั้งครั้งเดียวจบ ส่วนใครที่เครื่องมีหลายคนและต้องการสิทธิ์ต่างกัน ควรไปดูทาง ACL แทน
ขั้นตรวจก่อนบอกว่าเสร็จ
- รัน
testparmต้องไม่มีคำเตือนเรื่องค่าที่ไม่รู้จัก และต้องเห็นชื่อ share ที่ตั้งไว้ - รัน
testparm -s | grep -i bindต้องเห็นbind interfaces only = Yesจากค่าที่ Samba ตีความจริง - รัน
ss -ltnp | grep smbdต้องไม่เห็น0.0.0.0:445 - จาก Windows map ไดรฟ์ แล้วสร้างไฟล์ทดสอบ กลับไปดู
ls -lบนเซิร์ฟเวอร์ ต้องเป็นเจ้าของdebian-transmission - ลองลบไฟล์เดียวกันจากฝั่ง Transmission หรือด้วย user ของ daemon ต้องลบได้ และลบไฟล์ที่ Transmission สร้างจาก Windows ต้องได้ด้วย
Samba พื้นฐาน
- เจ้าของไฟล์ตาม user ที่ล็อกอิน
- สองฝั่งลบของกันไม่ได้
- ฟังทุก NIC ที่เครื่องมี
- ต้องไล่ chown ทีหลังเรื่อย ๆ
force user + bind
- เจ้าของไฟล์เป็น user ของ daemon
- สองฝั่งแก้และลบได้
- ฟังเฉพาะ lo กับ LAN
- ตรวจผลจริงได้ด้วย ss
ก่อนปิดเรื่อง ขอเล่าพฤติกรรมฝั่ง Windows ที่ทำให้งงได้ตอนแก้ config ถ้าไดรฟ์ Z: ต่ออยู่ก่อนแล้วผมไปแก้ smb.conf เซสชันเดิมยังคงถือค่าเก่า การทดสอบหลังแก้ต้องตัดการเชื่อมต่อเดิมก่อน (net use Z: /delete) แล้ว map ใหม่ ไม่งั้นจะนึกว่าแก้ไม่ได้ผล ทั้งที่แค่ยังไม่ได้ต่อใหม่ อีกข้อคือเมื่อเปลี่ยนรหัสด้วย smbpasswd ต้องลบ credential ที่เก็บไว้ด้วย cmdkey /delete:192.168.10.52 แล้วเพิ่มใหม่ มิฉะนั้น Windows จะส่งรหัสเก่าซ้ำไปจนบัญชีล็อกหรือขึ้นข้อความปฏิเสธซ้ำ ๆ ทั้งสองข้อเป็นเรื่องทั่วไปของการใช้ไดรฟ์เครือข่าย ไม่ได้เจาะจงกับเคสนี้
สิ่งที่ผมไม่ได้ทดสอบ
ผมไม่ได้วัดประสิทธิภาพการส่งไฟล์ ไม่ได้ลองกับ macOS หรือ Linux เป็นฝั่ง client และไม่ได้ทดสอบกับ Windows 11 ที่ปิด SMB1 ด้วยตัวเอง ที่บันทึกไว้คือเครื่อง Windows ฝั่งผมต่อได้ด้วย SMB2 ขึ้นไปและไม่มี guest ใครจะเอาสูตรนี้ไปใช้กับ client อื่นก็ต้องลองเองเลยครับ ส่วนเรื่องสิทธิ์ไฟล์เชิงลึกกว่านี้ เช่น ใช้ POSIX ACL แทน force user ก็เป็นอีกทางที่ผมไม่ได้เลือกในเคสนี้ ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
อยากให้เราทำให้ไหม?
ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย