Izinkan proses non-root mengikat ke port 80 dan 443?

104

Apakah mungkin untuk menyetel parameter kernel untuk memungkinkan program userland untuk mengikat port 80 dan 443?

Alasan saya bertanya adalah saya pikir itu bodoh untuk membiarkan proses istimewa membuka soket dan mendengarkan. Apa pun yang membuka soket dan mendengarkan adalah risiko tinggi, dan aplikasi berisiko tinggi tidak boleh berjalan sebagai root.

Saya lebih suka mencoba mencari tahu apa proses tidakrivasi mendengarkan pada port 80 daripada mencoba untuk menghapus malware yang bersembunyi dengan hak akses root.

jww
sumber
1
Lihat serverfault.com/questions/268099 dan stackoverflow.com/questions/413807 . Jawaban singkatnya adalah tidak.
Sami Laine
10
Jawaban panjangnya adalah ya .. jadi jawaban singkatnya harus ya juga.
BT
4
Jawaban singkatnya adalah ya.
Jason C

Jawaban:

163

Saya tidak yakin apa yang dimaksud dengan jawaban dan komentar lain di sini. Ini mungkin agak mudah. Ada dua opsi, keduanya memungkinkan akses ke port bernomor rendah tanpa harus meningkatkan proses root:

Opsi 1: Gunakan CAP_NET_BIND_SERVICEuntuk memberikan akses port bernomor rendah ke suatu proses:

Dengan ini, Anda dapat memberikan akses permanen ke biner tertentu untuk mengikat ke port bernomor rendah melalui setcapperintah:

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

Untuk detail lebih lanjut tentang bagian e / i / p, lihat cap_from_text.

Setelah melakukan ini, /path/to/binaryakan dapat mengikat ke port bernomor rendah. Perhatikan bahwa Anda harus menggunakan setcapbiner itu sendiri daripada symlink.

Opsi 2: Gunakan authbinduntuk memberikan akses satu kali, dengan kontrol pengguna / grup / port yang lebih baik:

Alat authbind( halaman manual ) ada tepat untuk ini.

  1. Instal authbindmenggunakan pengelola paket favorit Anda.

  2. Konfigurasikan untuk memberikan akses ke port yang relevan, misalnya untuk memungkinkan 80 dan 443 dari semua pengguna dan grup:

    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. Sekarang jalankan perintah Anda melalui authbind(tentukan opsional --deepatau argumen lain, lihat halaman manual):

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

    Misalnya

    authbind --deep java -jar SomeServer.jar
    

Ada kelebihan dan kekurangan untuk kedua hal di atas. Opsi 1 memberikan kepercayaan kepada biner tetapi tidak memberikan kontrol atas akses per-port. Opsi 2 memberikan kepercayaan kepada pengguna / grup dan memberikan kontrol atas akses per-port tetapi, AFAIK, hanya mendukung IPv4.

Jason C
sumber
Apakah itu benar-benar memerlukan rwxizin?
matt
Untuk mengembalikan operasi di Opsi 1, apakah Anda menjalankan perintah lagi menggunakan -pintead of +eip?
eugene1832
5
Berhati-hatilah bahwa, dengan setcap, jika Anda menimpa executable yang Anda berikan hak istimewa kepada (mis: lakukan pembangunan kembali) maka ia kehilangan status port yang diistimewakan dan Anda harus memberinya hak istimewa lagi: |
rogerdpack
1
Sesuatu yang harus kupikirkan; Saya mencoba menjalankan layanan sysv, yang menjalankan executable ruby ​​yang menggunakan ruby. Anda perlu memberikan setcapizin pada executable ruby ​​versi khusus , misalnya/usr/bin/ruby1.9.1
Christian Rondeau
3
Saya ragu bahwa chmoduntuk 777 byportfile adalah ide terbaik. Saya telah melihat memberikan izin mulai dari 500hingga 744. Saya akan terjebak dengan yang paling ketat yang bekerja untuk Anda.
Pere
28

Dale Hagglund tepat. Jadi saya hanya akan mengatakan hal yang sama tetapi dengan cara yang berbeda, dengan beberapa spesifik dan contoh. ☺

Hal yang benar untuk dilakukan di dunia Unix dan Linux adalah:

  • untuk memiliki program kecil, sederhana, mudah diaudit, yang berfungsi sebagai superuser dan mengikat soket pendengaran;
  • untuk memiliki program kecil, sederhana, mudah diaudit, yang menghilangkan hak istimewa, yang dihasilkan oleh program pertama;
  • untuk mendapatkan daging dari layanan, dalam program ketiga yang terpisah , berjalan di bawah akun non-superuser dan rantai dimuat oleh program kedua, berharap untuk hanya mewarisi deskriptor file terbuka untuk soket.

Anda memiliki gagasan yang salah tentang di mana risiko tinggi berada. Risiko tinggi adalah membaca dari jaringan dan bertindak berdasarkan apa yang dibaca bukan dalam tindakan sederhana membuka soket, mengikatnya ke port, dan menelepon listen(). Ini adalah bagian dari layanan yang melakukan komunikasi aktual yang berisiko tinggi. Bagian-bagian yang membuka,, bind()dan listen(), dan bahkan (sampai batas tertentu) bagian itu accepts(), tidak berisiko tinggi dan dapat dijalankan di bawah pengawasan superuser. Mereka tidak menggunakan dan bertindak atas (dengan pengecualian alamat IP sumber dalam accept()kasus ini) data yang berada di bawah kendali orang asing yang tidak dipercaya melalui jaringan.

Ada banyak cara untuk melakukan ini.

inetd

Seperti kata Dale Hagglund, "superserver jaringan" lama inetdmelakukan ini. Akun tempat proses layanan dijalankan adalah salah satu kolom di inetd.conf. Itu tidak memisahkan bagian mendengarkan dan bagian hak istimewa jatuh ke dalam dua program terpisah, kecil dan mudah diaudit, tetapi itu memisahkan kode layanan utama menjadi program terpisah, exec()ed dalam proses layanan yang muncul dengan deskriptor file terbuka untuk soket.

Kesulitan mengaudit tidak begitu menjadi masalah, karena seseorang hanya perlu mengaudit satu program. inetdMasalah utama adalah tidak terlalu banyak mengaudit tetapi lebih karena tidak memberikan kontrol layanan runtime sederhana yang halus, dibandingkan dengan alat yang lebih baru.

UCSPI-TCP dan daemontools

Paket UCSPI-TCP dan daemontools dari Daniel J. Bernstein dirancang untuk melakukan hal ini bersamaan. Satu alternatif dapat menggunakan toolset daemontools-encore Bruce Guenter yang sebagian besar setara .

Program untuk membuka deskriptor file socket dan mengikat ke port lokal istimewa adalah tcpserver, dari UCSPI-TCP. Itu baik listen()dan accept().

tcpserverkemudian memunculkan salah satu program layanan yang menjatuhkan hak root itu sendiri (karena protokol yang dilayani melibatkan mulai sebagai superuser dan kemudian "masuk", seperti halnya dengan, misalnya, FTP atau daemon SSH) atau setuidgidyang merupakan program kecil mandiri dan mudah diaudit yang semata-mata menjatuhkan hak istimewa dan kemudian mengaitkan muatan ke program layanan dengan semestinya (tidak ada bagian yang dengan demikian dijalankan dengan hak pengguna super, seperti halnya dengan, katakanlah, qmail-smtpd).

Jadi runskrip layanan misalnya (ini untuk dummyidentd karena menyediakan layanan IDENT nol):

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

tidak

Paket nosh saya dirancang untuk melakukan ini. Ini memiliki setuidgidutilitas kecil , sama seperti yang lain. Satu perbedaan kecil adalah bahwa itu dapat digunakan dengan systemd-style "LISTEN_FDS" layanan serta dengan layanan UCSPI-TCP, sehingga tcpserverprogram tradisional digantikan oleh dua program terpisah: tcp-socket-listendan tcp-socket-accept.

Sekali lagi, utilitas serba guna menelurkan dan saling memuat satu sama lain. Satu kekhasan desain yang menarik adalah bahwa seseorang dapat kehilangan hak superuser setelah listen()tetapi bahkan sebelum accept(). Berikut runskrip untuk qmail-smtpditu memang melakukan hal itu:

#!/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 \
'

Program yang dijalankan di bawah naungan superuser adalah alat rantai-loading layanan-agnostik kecil fdmove, clearenv, envdir, softlimit, tcp-socket-listen, dan setuidgid. Pada titik yang shdimulai, soket terbuka dan terikat ke smtpport, dan proses tidak lagi memiliki hak pengguna super.

s6, s6-networking, dan execline

Paket jaringan s6 dan s6 Laurent Bercot dirancang untuk melakukan hal ini bersamaan. Perintah-perintah secara struktural sangat mirip dengan daemontoolsdan dari UCSPI-TCP.

runskrip akan sama, kecuali untuk pengganti s6-tcpserveruntuk tcpserverdan s6-setuidgiduntuk setuidgid. Namun, orang mungkin juga memilih untuk menggunakan toolset execline M. Bercot pada saat yang sama.

Berikut ini adalah contoh dari layanan FTP, sedikit dimodifikasi dari aslinya Wayne Marshall , yang menggunakan execline, s6, s6-networking, dan program server FTP dari 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 adalah toolset lain yang berjalan di sepanjang jalur yang sama dengan ucspi-tcp dan s6-networking. Alatnya ada chpstdan tcpsvdkali ini, tetapi mereka melakukan hal yang sama, dan kode risiko tinggi yang melakukan pembacaan, pemrosesan, dan penulisan hal-hal yang dikirim melalui jaringan oleh klien yang tidak terpercaya masih dalam program terpisah.

Berikut contoh M. Pape menjalankan fnorddalam runskrip:

#!/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, pengawasan layanan baru dan sistem init yang dapat ditemukan di beberapa distribusi Linux, dimaksudkan untuk melakukan apa yang inetdbisa dilakukan . Namun, itu tidak menggunakan serangkaian program mandiri kecil. systemdSayangnya, kita harus mengaudit secara keseluruhan.

Dengan systemdsatu menciptakan file konfigurasi untuk menentukan soket yang systemdmendengarkan, dan layanan yang systemddimulai. File "unit" layanan memiliki pengaturan yang memungkinkan seseorang mengendalikan proses layanan, termasuk pengguna yang dijalankannya.

Dengan pengguna yang ditetapkan sebagai bukan pengguna super, systemdmelakukan semua pekerjaan membuka soket, mengikatnya ke port, dan memanggil listen()(dan, jika perlu, accept()) dalam proses # 1 sebagai superuser, dan proses layanan yang dilakukan memunculkan berjalan tanpa hak pengguna super.

JdeBP
sumber
2
Terima kasih atas pujian. Ini adalah koleksi saran konkret yang bagus. +1.
Dale Hagglund
11

Saya memiliki pendekatan yang agak berbeda. Saya ingin menggunakan port 80 untuk server node.js. Saya tidak dapat melakukannya karena Node.js diinstal untuk pengguna non-sudo. Saya mencoba menggunakan symlink, tetapi tidak berhasil untuk saya.

Kemudian saya mengetahui bahwa saya dapat meneruskan koneksi dari satu port ke port lainnya. Jadi saya memulai server pada port 3000 dan mengatur port forward dari port 80 ke port 3000.

Tautan ini memberikan perintah aktual yang dapat digunakan untuk melakukan ini. Ini perintahnya -

localhost / loopback

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

luar

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

Saya telah menggunakan perintah kedua dan itu berhasil untuk saya. Jadi saya pikir ini adalah jalan tengah untuk tidak mengizinkan proses pengguna untuk mengakses port yang lebih rendah secara langsung, tetapi memberi mereka akses menggunakan port-forwarding.

cupu
sumber
6
+1 untuk berpikir di luar kotak
Richard Wiseman
4

Naluri Anda sepenuhnya benar: adalah ide yang buruk untuk menjalankan program kompleks yang besar sebagai root, karena kompleksitasnya membuat mereka sulit dipercaya.

Tapi, itu juga ide yang buruk untuk memungkinkan pengguna biasa untuk mengikat port istimewa, karena port tersebut biasanya merupakan layanan sistem yang penting.

Pendekatan standar untuk menyelesaikan kontradiksi yang tampak ini adalah pemisahan hak istimewa . Ide dasarnya adalah untuk memisahkan program Anda menjadi dua (atau lebih) bagian, yang masing-masing berfungsi dengan baik dari keseluruhan aplikasi, dan yang berkomunikasi dengan antarmuka terbatas yang sederhana.

Dalam contoh yang Anda berikan, Anda ingin memisahkan program Anda menjadi dua bagian. Salah satu yang berjalan sebagai root dan terbuka dan mengikat ke soket istimewa, dan kemudian menyerahkannya entah bagaimana ke bagian lain, yang berjalan sebagai pengguna biasa.

Dua cara utama untuk mencapai pemisahan ini.

  1. Satu program yang dimulai sebagai root. Hal pertama yang dilakukannya adalah membuat soket yang diperlukan, sesederhana dan sebatas mungkin. Kemudian, itu menjatuhkan hak istimewa, yaitu, itu mengubah dirinya menjadi proses mode pengguna biasa, dan melakukan semua pekerjaan lainnya. Menjatuhkan hak istimewa dengan benar itu sulit, jadi silakan luangkan waktu untuk mempelajari cara yang benar untuk melakukannya.

  2. Sepasang program yang berkomunikasi melalui pasangan soket yang dibuat oleh proses induk. Program driver non-istimewa menerima argumen awal dan mungkin melakukan beberapa validasi argumen dasar. Ini menciptakan sepasang soket yang terhubung melalui socketpair (), dan kemudian bercabang dan mengeksekusi dua program lain yang akan melakukan pekerjaan nyata, dan berkomunikasi melalui pasangan soket. Salah satunya adalah privilege dan akan membuat socket server, dan operasi privilege lainnya, dan lainnya akan melakukan eksekusi aplikasi yang lebih kompleks dan karenanya kurang dapat dipercaya.

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

Dale Hagglund
sumber
Apa yang Anda usulkan tidak dianggap praktik terbaik. Anda mungkin melihat inetd, yang dapat mendengarkan pada soket istimewa dan kemudian menyerahkan soket itu ke program yang tidak terjangkau.
Dale Hagglund
3

Solusi paling sederhana: hapus semua port istimewa di linux

Bekerja di ubuntu / debian:

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

(berfungsi dengan baik untuk VirtualBox dengan akun non-root)

Sekarang, berhati-hatilah dengan keamanan karena semua pengguna dapat mengikat semua port!

soleuu
sumber
Itu pintar. Satu nit kecil: konfigurasi terbuka 80 dan 443, tetapi juga membuka semua port lain. Izin santai di port lain mungkin tidak diinginkan.
jww