อนุญาตให้กระบวนการที่ไม่ใช่รูทเชื่อมโยงกับพอร์ต 80 และ 443 หรือไม่


104

เป็นไปได้ไหมที่จะปรับพารามิเตอร์เคอร์เนลเพื่อให้โปรแกรม userland เชื่อมกับพอร์ต 80 และ 443 ได้?

เหตุผลที่ฉันถามคือฉันคิดว่ามันโง่ที่จะอนุญาตให้กระบวนการพิเศษเปิดซ็อกเก็ตและฟัง สิ่งใดก็ตามที่เปิดซ็อกเก็ตและฟังมีความเสี่ยงสูงและแอปพลิเคชันที่มีความเสี่ยงสูงไม่ควรทำงานในฐานะรูท

ฉันอยากลองคิดดูว่ากระบวนการที่ไม่มีสิทธิพิเศษกำลังฟังอยู่ที่พอร์ต 80 แทนที่จะพยายามลบมัลแวร์ที่ฝังอยู่ด้วยสิทธิ์พิเศษของรูท


1
ดูserverfault.com/questions/268099และstackoverflow.com/questions/413807 คำตอบสั้น ๆ คือไม่
Sami Laine

10
คำตอบที่ยาวคือใช่ .. ดังนั้นคำตอบสั้น ๆ ก็ควรเป็นเช่นกัน
BT

4
คำตอบสั้น ๆคือใช่
Jason C

คำตอบ:


163

ฉันไม่แน่ใจว่าคำตอบและความคิดเห็นอื่น ๆ ที่นี่อ้างถึงคืออะไร เป็นไปได้ค่อนข้างง่าย มีสองตัวเลือกซึ่งทั้งสองอย่างนี้อนุญาตให้เข้าถึงพอร์ตที่มีหมายเลขต่ำโดยไม่ต้องยกระดับกระบวนการให้รูท:

ตัวเลือก 1: ใช้CAP_NET_BIND_SERVICEเพื่อให้สิทธิ์การเข้าถึงพอร์ตที่มีหมายเลขต่ำแก่กระบวนการ

ด้วยวิธีนี้คุณสามารถให้สิทธิ์การเข้าถึงถาวรไปยังไบนารีที่ระบุเพื่อผูกเข้ากับพอร์ตที่มีหมายเลขต่ำผ่านsetcapคำสั่ง:

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ E / i / p cap_from_textส่วนหนึ่งดู

หลังจากทำเช่นนี้/path/to/binaryจะสามารถผูกเข้ากับพอร์ตที่มีหมายเลขต่ำได้ โปรดทราบว่าคุณต้องใช้setcapกับไบนารีเองแทนที่จะเป็น symlink

ตัวเลือก 2: ใช้authbindเพื่อให้สิทธิ์การเข้าถึงครั้งเดียวด้วยการควบคุมผู้ใช้ / กลุ่ม / พอร์ตที่ละเอียดยิ่งขึ้น:

เครื่องมือauthbind( man page ) มีอยู่จริงสำหรับสิ่งนี้

  1. ติดตั้งauthbindโดยใช้ตัวจัดการแพ็คเกจที่คุณชื่นชอบ

  2. กำหนดค่าเพื่อให้สามารถเข้าถึงพอร์ตที่เกี่ยวข้องเช่นอนุญาต 80 และ 443 จากผู้ใช้และกลุ่มทั้งหมด:

    sudo touch /etc/authbind/byport/80
    sudo touch /etc/authbind/byport/443
    sudo chmod 777 /etc/authbind/byport/80
    sudo chmod 777 /etc/authbind/byport/443
    
  3. ตอนนี้ดำเนินการคำสั่งของคุณผ่านทางauthbind(ระบุเป็นทางเลือก--deepหรือข้อโต้แย้งอื่น ๆ ดูหน้าคน):

    authbind --deep /path/to/binary command line args
    

    เช่น

    authbind --deep java -jar SomeServer.jar
    

มีด้านบนและด้านล่างทั้งสองด้านบน ตัวเลือก 1 มอบความไว้วางใจให้กับไบนารีแต่ไม่ได้ให้การควบคุมการเข้าถึงต่อพอร์ต ตัวเลือก 2 มอบความไว้วางใจให้กับผู้ใช้ / กลุ่มและให้การควบคุมการเข้าถึงต่อพอร์ต แต่ AFAIK รองรับเฉพาะ IPv4


จำเป็นต้องrwxได้รับอนุญาตหรือไม่?
แมตต์

หากต้องการคืนค่าการดำเนินการในตัวเลือก 1 คุณจะเรียกใช้คำสั่งอีกครั้งโดยใช้คำสั่ง-pintead +eipหรือไม่?
eugene1832

5
ระวังว่าด้วย setcap ถ้าคุณเขียนทับปฏิบัติการที่คุณให้สิทธิ์ในการ (เช่น: สร้างใหม่) แล้วมันจะสูญเสียสถานะพอร์ตสิทธิพิเศษและคุณต้องให้สิทธิ์อีกครั้ง: |
rogerdpack

1
สิ่งที่ฉันต้องทำด้วย ฉันพยายามเรียกใช้บริการ sysv ที่รันทับทิมที่ปฏิบัติการได้ซึ่งใช้ทับทิม คุณต้องให้setcapสิทธิ์ในการปฏิบัติการทับทิมเฉพาะเวอร์ชันเช่น/usr/bin/ruby1.9.1
Christian Rondeau

3
ฉันมีข้อสงสัยว่าเมื่อchmodถึง 777 byportไฟล์เป็นความคิดที่ดีที่สุด ผมเคยเห็นให้ permisions ตั้งแต่การ500 744ฉันจะติดอยู่กับคนที่เข้มงวดที่สุดที่เหมาะกับคุณ
Pere

28

Dale Hagglund เป็นจุดบน ดังนั้นฉันจะพูดในสิ่งเดียวกัน แต่ในวิธีที่แตกต่างกันโดยเฉพาะและตัวอย่าง ☺

สิ่งที่ควรทำใน Unix และ Linux ในโลกคือ:

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

คุณมีความคิดผิด ๆ ว่าที่ใดมีความเสี่ยงสูง มีความเสี่ยงสูงอยู่ในการอ่านจากเครือข่ายและการปฏิบัติตามสิ่งที่อ่านlisten()ไม่ได้อยู่ในการกระทำที่เรียบง่ายของการเปิดซ็อกเก็ตที่มีผลผูกพันกับพอร์ตและโทร นี่เป็นส่วนหนึ่งของบริการที่ใช้สื่อสารจริงที่มีความเสี่ยงสูง ชิ้นส่วนที่เปิดbind()และlisten()และถึงแม้จะเป็นส่วนที่accepts()ไม่ได้มีความเสี่ยงสูงและสามารถทำงานภายใต้การควบคุมของ superuser ได้ พวกเขาไม่ได้ใช้และดำเนินการ (ยกเว้นที่อยู่ IP ต้นทางในaccept()กรณี) ข้อมูลที่อยู่ภายใต้การควบคุมของคนแปลกหน้าที่ไม่น่าเชื่อถือผ่านเครือข่าย

มีหลายวิธีในการทำเช่นนี้

inetd

ดังที่ Dale Hagglund กล่าวว่า "ซูเปอร์เซิร์ฟเวอร์เครือข่าย" รุ่นเก่าinetdทำสิ่งนี้ inetd.confบัญชีที่ขั้นตอนการบริการมีการเรียกใช้เป็นหนึ่งในคอลัมน์ใน มันไม่ได้แยกส่วนการฟังและสิทธิการดร็อปส่วนหนึ่งออกเป็นสองโปรแกรมเล็ก ๆ และตรวจสอบได้ง่าย แต่มันจะแยกรหัสบริการหลักออกเป็นโปรแกรมแยกต่างหากexec()ed ในกระบวนการบริการที่วางไข่ด้วยตัวอธิบายไฟล์ที่เปิด สำหรับซ็อกเก็ต

ความยากในการตรวจสอบไม่ใช่ปัญหามากนักเนื่องจากมีเพียงการตรวจสอบโปรแกรมเดียวเท่านั้น inetdปัญหาที่สำคัญของการตรวจสอบไม่มาก แต่ค่อนข้างจะไม่ให้การควบคุมบริการรันไทม์แบบละเอียดอย่างง่ายเปรียบเทียบกับเครื่องมือล่าสุด

UCSPI-TCP และ daemontools

แพคเกจUCSPI-TCPและdaemontoolsของ Daniel J. Bernstein ได้รับการออกแบบให้ทำงานร่วมกัน หนึ่งสามารถใช้ชุดเครื่องมือdaemontools-encore ที่เทียบเท่ากับ Bruce Guenter ได้

โปรแกรมที่จะเปิดตัวอธิบายไฟล์ซ็อกเก็ตและเชื่อมโยงกับพอร์ตในเครื่องที่มีสิทธิใช้งานคือtcpserverจาก UCSPI-TCP มันไม่ทั้งในและlisten()accept()

tcpserverแล้ว spawns ทั้งโปรแกรมบริการที่ลดลงสิทธิ์ root ตัวเอง (เพราะโปรโตคอลที่ทำหน้าที่เกี่ยวข้องกับการเริ่มต้นจากการเป็น superuser แล้ว "เข้าสู่ระบบ" เช่นเดียวกับกรณีที่มีการยกตัวอย่างเช่นการ FTP หรือภูต SSH) หรือsetuidgidซึ่งเป็น โปรแกรมที่มีขนาดเล็กและตรวจสอบได้ง่ายในตัวเองซึ่งจะลดสิทธิ์ แต่เพียงผู้เดียวแล้วโหลดเครือข่ายไปยังโปรแกรมบริการที่เหมาะสม (ไม่มีส่วนใดของโปรแกรมนี้ทำงานด้วยสิทธิ์ superuser อย่างที่เป็นอยู่ในกรณีนี้ด้วยqmail-smtpd)

runสคริปต์บริการจะเป็นตัวอย่าง (สคริปต์นี้สำหรับdummyidentdสำหรับการให้บริการ IDENT ที่ว่าง):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

Nosh

แพ็คเกจ nosh ของฉันออกแบบมาเพื่อทำสิ่งนี้ มันมีsetuidgidยูทิลิตี้ขนาดเล็กเช่นเดียวกับคนอื่น ๆ หนึ่งความแตกต่างเล็กน้อยก็คือว่ามันสามารถใช้งานได้กับsystemd"LISTEN_FDS" สไตล์การบริการเช่นเดียวกับบริการ UCSPI-TCP ดังนั้นดั้งเดิมtcpserverโปรแกรมจะถูกแทนที่ด้วยสองโปรแกรมแยก: และtcp-socket-listentcp-socket-accept

อีกครั้งยูทิลิตี้วางไข่เดี่ยวและโหลดเชนอีกอัน หนึ่งมุมแหลมที่น่าสนใจของการออกแบบเป็นที่หนึ่งสามารถวางสิทธิ์ superuser หลังจากที่แต่ก่อนที่จะได้listen() accept()นี่คือrunสคริปต์สำหรับqmail-smtpdสิ่งที่ทำสิ่งนั้นอย่างแท้จริง:

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

โปรแกรมที่ทำงานภายใต้การอุปถัมภ์ของ superuser ที่มีการบริการที่ไม่เชื่อเรื่องพระเจ้าเครื่องมือห่วงโซ่โหลดขนาดเล็กfdmove, clearenv, envdir, softlimit, และtcp-socket-listen setuidgidเมื่อถึงจุดshเริ่มต้นซ็อกเก็ตจะเปิดและเชื่อมโยงกับsmtpพอร์ตและกระบวนการจะไม่มีสิทธิ์ superuser อีกต่อไป

s6, เครือข่าย s6 และดำเนินการ

แพ็คเกจเครือข่ายs6และเครือข่ายs6ของ Laurent Bercot ได้รับการออกแบบให้ทำงานร่วมกัน คำสั่งมีโครงสร้างคล้ายกันมากกับคำสั่งdaemontoolsและ UCSPI-TCP

runสคริปต์จะมากเหมือนกันยกเว้นสำหรับการทดแทนของs6-tcpserverสำหรับtcpserverและสำหรับs6-setuidgid setuidgidอย่างไรก็ตามหนึ่งอาจเลือกที่จะใช้ประโยชน์จากชุดเครื่องมือexeclineของ M. Bercot ในเวลาเดียวกัน

นี่คือตัวอย่างของบริการ FTP, แก้ไขเบา ๆ จากต้นฉบับของ Wayne Marshallที่ใช้ execline, s6, s6-networking และโปรแกรมเซิร์ฟเวอร์ FTP จากpublicfile :

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

ipsvdของ Gerrit Pape เป็นชุดเครื่องมืออีกชุดที่ทำงานในบรรทัดเดียวกันกับ ucspi-tcp และ s6-networking เครื่องมือเป็นchpstและtcpsvdในเวลานี้ แต่พวกเขาทำสิ่งเดียวกันและรหัสความเสี่ยงสูงที่ทำการอ่านประมวลผลและเขียนสิ่งต่าง ๆ ที่ส่งผ่านเครือข่ายโดยลูกค้าที่ไม่น่าไว้วางใจยังคงอยู่ในโปรแกรมแยกต่างหาก

นี่คือตัวอย่างการทำงานของM. Papefnordในrunสคริปต์:

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd, การกำกับดูแลบริการใหม่และระบบการ init ที่สามารถพบได้ในลินุกซ์บางอย่างมีจุดมุ่งหมายที่จะทำในสิ่งที่inetdสามารถทำได้ อย่างไรก็ตามมันไม่ได้ใช้ชุดโปรแกรมขนาดเล็กที่มีในตัวเอง หนึ่งมีการตรวจสอบsystemdอย่างครบถ้วนน่าเสียดาย

ด้วยsystemdหนึ่งสร้างไฟล์การกำหนดค่าเพื่อกำหนดซ็อกเก็ตที่systemdฟังและบริการที่systemdเริ่มต้น ไฟล์ "หน่วย" บริการมีการตั้งค่าที่ช่วยให้สามารถควบคุมกระบวนการบริการได้อย่างมากรวมถึงผู้ใช้ที่เรียกใช้

เมื่อผู้ใช้ตั้งค่าให้ไม่ใช่ผู้ใช้ขั้นสูงsystemdจะทำงานทั้งหมดของการเปิดซ็อกเก็ตผูกเข้ากับพอร์ตและการโทรlisten()(และหากจำเป็นaccept()) ในกระบวนการ # 1 ในฐานะผู้ใช้ขั้นสูงและกระบวนการบริการที่มัน วางไข่ทำงานโดยไม่มีสิทธิ์ superuser


2
ขอบคุณสำหรับคำอวยพร. นี่คือคำแนะนำที่เป็นรูปธรรม +1
Dale Hagglund

11

ฉันมีวิธีการที่ค่อนข้างแตกต่างกัน ฉันต้องการใช้พอร์ต 80 สำหรับเซิร์ฟเวอร์ node.js ฉันไม่สามารถทำได้เนื่องจากติดตั้ง Node.js สำหรับผู้ใช้ที่ไม่ใช่ sudo ฉันพยายามใช้ symlink แต่มันไม่ได้ผลสำหรับฉัน

จากนั้นฉันก็รู้ว่าฉันสามารถส่งต่อการเชื่อมต่อจากพอร์ตหนึ่งไปยังอีกพอร์ตหนึ่งได้ ดังนั้นฉันจึงเริ่มเซิร์ฟเวอร์บนพอร์ต 3000 และตั้งค่าพอร์ตไปข้างหน้าจากพอร์ต 80 ถึงพอร์ต 3000

ลิงค์นี้ให้คำสั่งจริงที่สามารถใช้ทำสิ่งนี้ได้ นี่คือคำสั่ง -

localhost / ย้อนกลับ

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

ภายนอก

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

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


6
+1 เมื่อคิดนอกกรอบ
Richard Wiseman

4

สัญชาตญาณของคุณถูกต้องทั้งหมด: เป็นความคิดที่ดีที่จะให้โปรแกรมที่ซับซ้อนขนาดใหญ่ทำงานเหมือนรูทเพราะความซับซ้อนของมันทำให้พวกเขายากที่จะเชื่อใจ

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

วิธีการมาตรฐานในการแก้ปัญหาความขัดแย้งนี้จะแยกสิทธิพิเศษ แนวคิดพื้นฐานคือการแยกโปรแกรมของคุณออกเป็นสองส่วน (หรือมากกว่า) แต่ละส่วนทำหน้าที่กำหนดส่วนที่ดีของแอพพลิเคชั่นโดยรวมและสื่อสารโดยอินเทอร์เฟซที่ จำกัด อย่างง่าย

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

ทั้งสองวิธีหลักเพื่อให้ได้การแยกนี้

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

  2. คู่ของโปรแกรมที่สื่อสารผ่านซ็อกเก็ตคู่ที่สร้างขึ้นโดยกระบวนการหลัก โปรแกรมไดรเวอร์ที่ไม่ได้รับสิทธิพิเศษจะได้รับอาร์กิวเมนต์เริ่มต้นและอาจทำการตรวจสอบอาร์กิวเมนต์พื้นฐานบางอย่าง มันสร้างซ็อกเก็ตที่เชื่อมต่อคู่ผ่าน socketpair () จากนั้นแยกและเรียกใช้โปรแกรมอื่นอีกสองโปรแกรมที่จะทำงานจริงและสื่อสารผ่านซ็อกเก็ตคู่ หนึ่งในนั้นคือสิทธิพิเศษและจะสร้างเซิร์ฟเวอร์ซ็อกเก็ตและการดำเนินการพิเศษอื่น ๆ และอื่น ๆ จะมีความซับซ้อนมากขึ้นและการดำเนินการแอปพลิเคชันที่น่าเชื่อถือน้อยลง

[1] http://en.m.wikipedia.org/wiki/Privilege_separation


สิ่งที่คุณเสนอไม่ถือว่าเป็นแนวปฏิบัติที่ดีที่สุด คุณอาจดู inetd ซึ่งสามารถฟังบนซ็อกเก็ตที่มีสิทธิใช้งานและมอบซ็อกเก็ตนั้นให้กับโปรแกรมที่ไม่มีสิทธิใช้งาน
Dale Hagglund

3

วิธีที่ง่ายที่สุด: ลบพอร์ตที่มีสิทธิ์ทั้งหมดบน linux

ทำงานบน Ubuntu / debian:

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(ทำงานได้ดีสำหรับ VirtualBox ด้วยบัญชีที่ไม่ใช่รูท)

ตอนนี้ระวังเรื่องความปลอดภัยเพราะผู้ใช้ทุกคนสามารถผูกพอร์ตทั้งหมดได้!


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