เหตุใดฉันจึงต้อง chroot สำหรับ sandboxing เพื่อความปลอดภัยถ้าแอปพลิเคชันของฉันสามารถเรียกใช้ตั้งแต่เริ่มต้นที่ระดับต่ำกว่าได้


14

ฉันกำลังเขียน daemon เซิร์ฟเวอร์ HTTP ใน C (มีเหตุผลว่าด้วยอะไร) จัดการมันด้วยไฟล์ systemd unit

ฉันเขียนใหม่แอปพลิเคชันที่ออกแบบมาเมื่อ 20 ปีก่อนประมาณปี 1995 และระบบที่พวกเขาใช้คือพวกเขา chroot แล้ว setuid และขั้นตอนมาตรฐาน

ตอนนี้ในงานก่อนหน้าของฉันนโยบายปกติคือคุณไม่เคยเรียกใช้กระบวนการใด ๆ ในฐานะที่เป็นรูท คุณสร้างผู้ใช้ / กลุ่มสำหรับมันและเรียกใช้จากที่นั่น แน่นอนว่าระบบใช้บางสิ่งเป็นรูท แต่เราสามารถบรรลุการประมวลผลเชิงตรรกะทางธุรกิจทั้งหมดโดยไม่ต้องรูท

ตอนนี้สำหรับดีมอน HTTP, ฉันสามารถเรียกใช้โดยไม่รูทถ้าฉันไม่ chroot ภายในแอปพลิเคชัน. ดังนั้นแอปพลิเคชันจะปลอดภัยกว่าหรือไม่ที่จะไม่รันในฐานะรูท?

ปลอดภัยหรือไม่ที่จะเรียกใช้ในฐานะผู้ใช้ mydaemon ตั้งแต่ต้น? แทนที่จะเริ่มด้วย root, chrooting, จากนั้น setuid เป็น mydaemon-user


3
คุณจำเป็นต้องเรียกใช้ในฐานะรูทเพื่อใช้พอร์ต 80 หรือ 443 มิฉะนั้นคุณสามารถทำสิ่งที่ tomcat และซอฟต์แวร์ webapp / เว็บเซิร์ฟเวอร์อื่น ๆ ทำและทำงานบนพอร์ตที่สูงกว่า (เช่น 8080, 9090 ฯลฯ ) จากนั้นใช้อย่างใดอย่างหนึ่ง apache / nginx เพื่อเชื่อมต่อพร็อกซีกับซอฟต์แวร์เว็บเซิร์ฟเวอร์ของคุณหรือใช้ไฟร์วอลล์ของระบบเพื่อ NAT / ส่งต่อทราฟฟิกไปยังเว็บเซิร์ฟเวอร์ของคุณจากพอร์ต 80 หากคุณไม่ต้องการพอร์ต 80 หรือ 443 หรือสามารถพร็อกซีหรือส่งต่อการเชื่อมต่อ จากนั้นคุณไม่จำเป็นต้องเรียกใช้เป็นรูทใน chroot หรืออย่างอื่น
SnakeDoc

3
@SnakeDoc บน Linux ไม่เป็นความจริงอีกต่อไป capabilities(7)ด้วย
0xC0000022L

@SnakeDoc คุณสามารถใช้authbind ได้เช่นกัน
Abdul Ahad

คำตอบ:


27

ดูเหมือนว่าคนอื่นพลาดจุดของคุณซึ่งไม่ใช่เหตุผลว่าทำไมต้องใช้รูทที่เปลี่ยนแปลงซึ่งแน่นอนว่าคุณรู้อยู่แล้วอย่างชัดเจนและคุณสามารถทำอะไรเพื่อ จำกัด ข้อ จำกัด ของdæmonsเมื่อคุณรู้อย่างชัดเจนเกี่ยวกับการทำงานภายใต้ aegides ของ บัญชีผู้ใช้ที่ไม่มีสิทธิพิเศษ แต่ทำไมจะทำสิ่งนี้ภายในโปรแกรม มีตัวอย่างที่ชัดเจนว่าทำไม

พิจารณาการออกแบบhttpdโปรแกรมdæmonในแพคเกจไฟล์สาธารณะของ Daniel J. Bernstein สิ่งแรกที่ทำคือเปลี่ยนรูทเป็นไดเร็กทอรีรูทที่ได้รับคำสั่งให้ใช้กับอาร์กิวเมนต์คำสั่งจากนั้นปล่อยสิทธิพิเศษให้กับ ID ผู้ใช้และ ID กลุ่มที่ไม่มีสิทธิพิเศษที่ถูกส่งผ่านตัวแปรสภาพแวดล้อมสองตัว

ชุดเครื่องมือการจัดการของDæmonมีเครื่องมือเฉพาะสำหรับสิ่งต่าง ๆ เช่นการเปลี่ยนไดเรกทอรีรากและเปลี่ยนเป็น ID ผู้ใช้และกลุ่มที่ไม่มีสิทธิพิเศษ runit Gerrit Pape chpstมี ชุดเครื่องมือ Nosh ของฉันมีและchroot setuidgid-fromenvS6 Laurent Bercot มีและs6-chroot s6-setuidgidเวย์นมาร์แชลล์ Perp มีและruntool runuidและอื่น ๆ แน่นอนว่าพวกเขาทุกคนมีชุดเครื่องมือ daemontools ของ M. Bernstein setuidgidซึ่งมีมาก่อน

ใครจะคิดว่าสามารถดึงฟังก์ชั่นการhttpdใช้งานและใช้งานเครื่องมือเฉพาะดังกล่าวได้ จากนั้นเมื่อคุณนึกภาพไม่มีส่วนใดของโปรแกรมเซิร์ฟเวอร์ที่เคยทำงานด้วยสิทธิ์ผู้ใช้ระดับสูง

ปัญหาคือว่าหนึ่งในผลโดยตรงต้องทำงานอย่างมีนัยสำคัญมากขึ้นในการตั้งค่ารูตที่เปลี่ยนแปลงและสิ่งนี้ทำให้เกิดปัญหาใหม่

ด้วย Bernstein httpdเนื่องจากเป็นเพียงไฟล์และไดเรกทอรีที่อยู่ในแผนผังไดเรกทอรีรากเท่านั้นที่จะเผยแพร่สู่โลก มีอะไรอย่างอื่นในต้นไม้ที่ทั้งหมด นอกจากนี้ยังมีเหตุผลที่ไม่มีใด ๆไฟล์ภาพโปรแกรมปฏิบัติการอยู่ในต้นไม้ที่

แต่ย้ายเปลี่ยนไดเรกทอรีรากออกเป็นโปรแกรมห่วงโซ่โหลด (หรือ systemd) และก็ไฟล์ภาพโปรแกรมสำหรับhttpdห้องสมุดที่ใช้ร่วมกันว่ามันโหลดและไฟล์พิเศษใด ๆ ใน/etc, /runและ/devที่โหลดโปรแกรมหรือ C รันไทม์การเข้าถึงห้องสมุด ในระหว่างการเริ่มต้นโปรแกรม (ซึ่งคุณอาจพบว่าค่อนข้างน่าแปลกใจหากคุณtruss/ straceโปรแกรม C หรือ C ++) ก็จะต้องมีอยู่ในรากที่เปลี่ยนแปลง ไม่เช่นhttpdนั้นไม่สามารถผูกมัดและไม่โหลด / เรียกใช้

จำไว้ว่านี่เป็นเซิร์ฟเวอร์เนื้อหา HTTP (S) มันอาจจะสามารถให้บริการไฟล์ใด ๆ (อ่านได้ทั่วโลก) ในรากที่มีการเปลี่ยนแปลง ตอนนี้รวมถึงสิ่งต่าง ๆ เช่นไลบรารีที่แบ่งใช้ตัวโหลดโปรแกรมของคุณและสำเนาของไฟล์การกำหนดค่าตัวโหลด / CRTL ต่างๆสำหรับระบบปฏิบัติการของคุณ และถ้าบางคน (โดยไม่ได้ตั้งใจ) หมายถึงเซิร์ฟเวอร์เนื้อหามีสิทธิ์เข้าถึงเพื่อเขียนข้อมูลเซิร์ฟเวอร์ที่ถูกบุกรุกอาจสามารถเข้าถึงการเขียนเพื่อเข้าถึงอิมเมจของโปรแกรมhttpdด้วยตัวเองหรือแม้แต่โปรแกรมโหลดเดอร์ของระบบของคุณ (อย่าลืมว่าตอนนี้คุณมีสองชุดขนาน/usr, /lib, /etc, /runและ/devไดเรกทอรีที่จะให้การรักษาความปลอดภัย.)

ไม่มีสิ่งใดในกรณีที่httpdการเปลี่ยนแปลงสิทธิการใช้งานของรูทและดรอป

ดังนั้นคุณได้ทำการซื้อขายโดยมีรหัสสิทธิพิเศษจำนวนเล็กน้อยซึ่งค่อนข้างง่ายต่อการตรวจสอบและทำงานทันทีที่เริ่มต้นhttpdโปรแกรมโดยใช้สิทธิ์ superuser สำหรับการมีพื้นผิวการโจมตีที่ขยายอย่างมากของไฟล์และไดเรกทอรีภายในรูทที่เปลี่ยนแปลง

นี่คือเหตุผลที่มันไม่ง่ายเหมือนการทำทุกอย่างกับโปรแกรมบริการ

ขอให้สังเกตว่านี่เป็นการทำงานขั้นต่ำสุดภายในhttpdตัวมันเอง ทั้งหมดของรหัสที่ไม่สิ่งต่างๆเช่นการดูในฐานข้อมูลบัญชีระบบปฏิบัติการสำหรับ ID ผู้ใช้และกลุ่ม ID จะนำไปสู่ผู้ตัวแปรสภาพแวดล้อมในสถานที่แรกคือภายนอกให้กับโปรแกรมในแบบสแตนด์อโลนคำสั่งตรวจสอบได้ง่ายเช่นhttpd envuidgid(และแน่นอนมันเป็นเครื่องมือที่ UCSPI ดังนั้นจึงมีไม่มีรหัสเพื่อฟังบนพอร์ต TCP ที่เกี่ยวข้อง (s) หรือยอมรับการเชื่อมต่อผู้ที่เป็นโดเมนของคำสั่งเช่นtcpserver, tcp-socket-listen, tcp-socket-accept, s6-tcpserver4-socketbinder, s6-tcpserver4dและอื่น ๆ .)

อ่านเพิ่มเติม


+1 ผิดที่คิด ฉันพบชื่อและย่อหน้าสุดท้ายที่คลุมเครือและหากคุณพูดถูกฉันก็พลาดประเด็นไป คำตอบนี้ให้การตีความที่เป็นประโยชน์มาก โดยส่วนตัวฉันจะทราบอย่างชัดเจนว่าต้องสร้างสภาพแวดล้อม chroot เช่นนี้เป็นความพยายามพิเศษที่คนส่วนใหญ่ต้องการหลีกเลี่ยง แต่จุดรักษาความปลอดภัย 2 จุดที่นี่ได้ถูกสร้างขึ้นมาอย่างดี
sourcejedi

อีกจุดที่ต้องจำคือถ้าเซิร์ฟเวอร์ตัดสิทธิพิเศษก่อนที่จะประมวลผลทราฟฟิกเครือข่ายใด ๆ รหัสสิทธิพิเศษจะไม่ถูกเปิดเผยต่อการโจมตีระยะไกลใด ๆ
kasperd

5

ฉันคิดว่ารายละเอียดคำถามของคุณมากมายสามารถนำไปใช้ได้อย่างเท่าเทียมกันavahi-daemonซึ่งฉันได้ดูเมื่อเร็ว ๆ นี้ (ฉันอาจพลาดรายละเอียดอื่น ๆ ที่แตกต่างไป) การรัน avahi-daemon ใน chroot มีข้อดีหลายประการในกรณีที่ avahi-daemon ถูกบุกรุก เหล่านี้รวมถึง:

  1. มันไม่สามารถอ่านไดเรกทอรีบ้านของผู้ใช้และลบข้อมูลส่วนตัว
  2. มันไม่สามารถใช้ประโยชน์จากข้อบกพร่องในโปรแกรมอื่น ๆ โดยการเขียนไปที่ / tmp มีอย่างน้อยหนึ่งประเภททั้งหมดของข้อบกพร่องดังกล่าว เช่นhttps://www.google.co.uk/search?q=tmp+race+security+bug
  3. มันไม่สามารถเปิดไฟล์ซ็อกเก็ตยูนิกซ์ใด ๆ ที่อยู่นอก chroot ซึ่ง daemons อื่น ๆ อาจฟังและอ่านข้อความ

จุดที่ 3 อาจดีเป็นพิเศษเมื่อคุณไม่ได้ใช้ dbus หรือคล้ายกัน ... ฉันคิดว่า avahi-daemon ใช้ dbus ดังนั้นจึงมั่นใจได้ว่าจะสามารถเข้าถึงระบบ dbus ได้แม้จะอยู่ใน chroot หากคุณไม่ต้องการความสามารถในการส่งข้อความบนระบบ dbus การปฏิเสธความสามารถนั้นอาจเป็นคุณลักษณะด้านความปลอดภัยที่ดีทีเดียว

จัดการมันด้วยไฟล์ systemd unit

โปรดทราบว่าถ้า Avahi-ภูตถูกเขียนใหม่ก็อาจจะเลือกที่จะพึ่งพา systemd ProtectHomeสำหรับการรักษาความปลอดภัยและการใช้งานเช่น ฉันเสนอการเปลี่ยนแปลง avahi-daemon เพื่อเพิ่มการป้องกันเหล่านี้เป็นเลเยอร์พิเศษพร้อมกับการป้องกันเพิ่มเติมบางอย่างที่ไม่รับประกันโดย chroot คุณสามารถดูรายการตัวเลือกทั้งหมดที่ฉันเสนอได้ที่นี่:

https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a

ดูเหมือนว่ามีข้อ จำกัด เพิ่มเติมซึ่งฉันสามารถใช้ได้หาก avahi-daemon ไม่ได้ใช้ chroot เองซึ่งบางอย่างถูกกล่าวถึงในข้อความยืนยัน ฉันไม่แน่ใจว่าสิ่งนี้ใช้

หมายเหตุการป้องกันที่ฉันใช้จะไม่ จำกัด daemon จากการเปิดไฟล์ซ็อกเก็ตยูนิกซ์ (จุดที่ 3 ด้านบน)

อีกวิธีคือการใช้ SELinux อย่างไรก็ตามคุณควรผูกแอปพลิเคชันของคุณไว้กับชุดย่อยของลินุกซ์ เหตุผลที่ฉันคิดว่า SELinux ในที่นี้คือ SELinux จำกัด การเข้าถึงที่โพรเซสมีบน dbus ในลักษณะที่ละเอียด ตัวอย่างเช่นฉันคิดว่าคุณมักจะคาดหวังว่าsystemdจะไม่อยู่ในรายการชื่อรถบัสที่คุณต้องการเพื่อส่งข้อความไปยัง :-)

"ฉันสงสัยว่าถ้าใช้ systemd sandbox ที่ปลอดภัยกว่า chroot / setuid / umask / ... "

สรุป: ทำไมไม่ทั้งสอง ลองถอดรหัสข้างบนเล็กน้อย :-)

หากคุณคิดเกี่ยวกับจุดที่ 3 การใช้ chroot จะช่วย จำกัด ขอบเขตมากขึ้น ProtectHome = และเพื่อน ๆ ก็ไม่ได้พยายามเข้มงวดเท่า chroot (ตัวอย่างเช่นไม่มีบัญชีดำตัวเลือก systemd ตัวเลือกชื่อ/runที่เรามักจะใส่ไฟล์ซ็อกเก็ตยูนิกซ์)

chroot แสดงให้เห็นว่าการ จำกัด การเข้าถึงระบบไฟล์อาจมีประสิทธิภาพมาก แต่ไม่ใช่ทุกอย่างบน Linux ที่เป็นไฟล์ :-) มีตัวเลือก systemd ที่สามารถ จำกัด สิ่งอื่น ๆ ที่ไม่ใช่ไฟล์ สิ่งนี้มีประโยชน์หากโปรแกรมถูกบุกรุกคุณสามารถลดคุณสมบัติเคอร์เนลที่มีให้ซึ่งมันอาจพยายามใช้ช่องโหว่ในระบบตัวอย่างเช่น avahi-daemon ไม่ต้องการซ็อกเก็ตบลูทู ธ และฉันเดาว่าเว็บเซิร์ฟเวอร์ของคุณไม่ได้ :-) ดังนั้นอย่าให้สิทธิ์เข้าถึงตระกูลที่อยู่ AF_BLUETOOTH เพียงแค่รายการที่อนุญาต AF_INET, AF_INET6 และอาจ AF_UNIX โดยใช้RestrictAddressFamilies=ตัวเลือก

โปรดอ่านเอกสารสำหรับแต่ละตัวเลือกที่คุณใช้ ตัวเลือกบางอย่างมีประสิทธิภาพมากขึ้นเมื่อรวมกับตัวเลือกอื่น ๆ และบางตัวเลือกนั้นไม่สามารถใช้ได้กับสถาปัตยกรรมซีพียูทั้งหมด (ไม่ใช่เพราะ CPU ไม่ดี แต่เนื่องจากพอร์ต Linux สำหรับ CPU นั้นไม่ได้ออกแบบมาอย่างดีฉันคิดว่า)

(มีหลักการทั่วไปที่นี่มีความปลอดภัยมากขึ้นถ้าคุณสามารถเขียนรายการสิ่งที่คุณต้องการอนุญาตไม่ใช่สิ่งที่คุณต้องการปฏิเสธเช่นเดียวกับการกำหนด chroot ให้รายชื่อไฟล์ที่คุณได้รับอนุญาตให้เข้าถึงและสิ่งนี้แข็งแกร่งกว่า กว่าจะบอกว่าคุณต้องการบล็อก/home)

โดยหลักการคุณสามารถใช้ข้อ จำกัด เดียวกันทั้งหมดด้วยตัวคุณเองก่อน setuid () มันเป็นแค่โค้ดที่คุณสามารถคัดลอกจาก systemd อย่างไรก็ตามตัวเลือกหน่วย systemd ควรเขียนได้ง่ายกว่ามากและเนื่องจากอยู่ในรูปแบบมาตรฐานจึงควรอ่านและตรวจสอบได้ง่ายขึ้น

ดังนั้นฉันขอแนะนำอย่างยิ่งให้อ่านเฉพาะในส่วน sandboxing ของman systemd.execบนแพลตฟอร์มเป้าหมายของคุณ แต่ถ้าคุณต้องการการออกแบบที่ปลอดภัยที่สุดที่เป็นไปได้ฉันจะไม่กลัวที่จะลองchroot(และแล้ววางrootสิทธิพิเศษ) ในโปรแกรมของคุณได้เป็นอย่างดี มีการแลกเปลี่ยนที่นี่ การใช้chrootข้อ จำกัด บางอย่างกับการออกแบบโดยรวมของคุณ หากคุณมีการออกแบบที่ใช้ chroot และดูเหมือนว่าจะทำสิ่งที่คุณต้องการมันฟังดูดีมาก


+1 โดยเฉพาะอย่างยิ่งสำหรับคำแนะนำ systemd
mattdm

ฉันเรียนรู้ค่อนข้างน้อยจากคำตอบของคุณถ้าสแต็คโอเวอร์โฟลว์อนุญาตให้ตอบได้หลายคำตอบฉันก็ยอมรับด้วยเช่นกัน ผมสงสัยว่าถ้าใช้ systemd Sandboxing ปลอดภัยมากกว่า chroot / setuid / umask / ...
ผนัง

@ ฉันดีใจที่คุณชอบมัน :) นั่นเป็นคำตอบที่เป็นธรรมชาติมากสำหรับคำตอบของฉัน ดังนั้นฉันจึงอัปเดตอีกครั้งเพื่อลองตอบคำถามของคุณ
sourcejedi

1

หากคุณสามารถพึ่งพา systemd ได้แน่นอนว่าปลอดภัยกว่า (และง่ายกว่า!) เพื่อปล่อยให้ sandbox ไปที่ systemd (แน่นอนแอปพลิเคชันยังสามารถตรวจสอบได้ว่ามีการเปิดตัวแซนด์บ็อกซ์โดย systemd หรือไม่และแซนด์บ็อกซ์เองถ้ามันยังคงอยู่ในรูท) เทียบเท่ากับบริการที่คุณอธิบาย:

[Service]
ExecStart=/usr/local/bin/mydaemon
User=mydaemon-user
RootDirectory=...

แต่เราไม่ต้องหยุดตรงนั้น systemd สามารถทำ sandboxing อื่น ๆ ให้คุณได้มากมาย - นี่คือตัวอย่าง:

[Service]
# allocate separate /tmp and /var/tmp for the service
PrivateTmp=yes
# mount / (except for some subdirectories) read-only
ProtectSystem=strict
# empty /home, /root
ProtectHome=yes
# disable setuid and other privilege escalation mechanisms
NoNewPrivileges=yes
# separate network namespace with only loopback device
PrivateNetwork=yes
# only unix domain sockets (no inet, inet6, netlink, …)
RestrictAddressFamilies=AF_UNIX

ดูman 5 systemd.execคำสั่งเพิ่มเติมและคำอธิบายโดยละเอียดเพิ่มเติม หากคุณสร้างซ็อกเก็ต daemon ให้เปิดใช้งาน ( man 5 systemd.socket) คุณสามารถใช้ตัวเลือกที่เกี่ยวข้องกับเครือข่าย: ลิงก์เดียวของบริการไปยังโลกภายนอกจะเป็นซ็อกเก็ตเครือข่ายที่ได้รับจาก systemd จะไม่สามารถเชื่อมต่อกับสิ่งอื่นได้ หากเป็นเซิร์ฟเวอร์ธรรมดาที่ฟังเฉพาะพอร์ตบางตัวและไม่จำเป็นต้องเชื่อมต่อกับเซิร์ฟเวอร์อื่น ๆ สิ่งนี้อาจมีประโยชน์ (ตัวเลือกที่เกี่ยวข้องกับระบบไฟล์ยังสามารถทำให้RootDirectoryล้าสมัยในความคิดของฉันดังนั้นบางทีคุณไม่จำเป็นต้องสร้างไดเรกทอรีรากใหม่ด้วยไบนารีและไลบรารีที่จำเป็นทั้งหมดอีกต่อไป)

เวอร์ชั่นใหม่กว่า systemd (ตั้งแต่ v232) รองรับDynamicUser=yesเช่นกันโดยที่ systemd จะจัดสรรผู้ใช้บริการให้คุณโดยอัตโนมัติสำหรับการใช้งานบริการเท่านั้น ซึ่งหมายความว่าคุณไม่ได้มีการลงทะเบียนผู้ใช้ถาวรสำหรับการให้บริการและการทำงานดีตราบเท่าที่บริการไม่ได้เขียนไปยังสถานที่ระบบไฟล์อื่น ๆ กว่าStateDirectory, LogsDirectoryและCacheDirectory(ซึ่งคุณยังสามารถประกาศในแฟ้มหน่วย - ดูman 5 systemd.execอีกครั้ง - และระบบใดที่จะจัดการจัดการให้กำหนดอย่างถูกต้องให้กับผู้ใช้แบบไดนามิก)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.