Apakah ada cara bagi proses non-root untuk mengikat port "istimewa" di Linux?


389

Sangat menyebalkan memiliki batasan ini pada kotak pengembangan saya, ketika tidak akan ada pengguna selain saya.

Saya mengetahui solusi standar , tetapi tidak ada yang melakukan persis apa yang saya inginkan:

  1. authbind (Versi dalam pengujian Debian, 1.0, hanya mendukung IPv4)
  2. Menggunakan target iptables REDIRECT untuk mengarahkan ulang port rendah ke port tinggi (tabel "nat" belum diterapkan untuk ip6tables, versi iptables IPv6)
  3. sudo (Menjalankan sebagai root adalah apa yang saya coba hindari)
  4. SELinux (atau serupa). (Ini hanya kotak dev saya, saya tidak ingin memperkenalkan banyak kompleksitas tambahan.)

Apakah ada beberapa sysctlvariabel sederhana untuk memungkinkan proses non-root untuk mengikat ke port "istimewa" (port kurang dari 1024) di Linux, atau apakah saya hanya kurang beruntung?

EDIT: Dalam beberapa kasus, Anda dapat menggunakan kemampuan untuk melakukan ini.


5
Dalam pengalaman saya, salah satu alasan untuk mencoba ini adalah untuk menulis server web daripada menggunakan Apache (atau lighttpd).
S.Lott

Saya telah menambahkan hal-hal setcap ke jawaban saya ke stackoverflow.com/questions/277991/…
Paul Tomblin

15
Karena dalam contoh ini, saya menggunakan IPv6, itulah sebabnya beberapa solusi "biasa" (authbind dan iptables REDIRECT) tidak bekerja untuk saya.
Jason Creighton


1
Ada beberapa cara untuk melakukannya. Lihat Membolehkan proses non-root untuk mengikat ke port 80 dan 443? pada Pengguna Super.
jww

Jawaban:


392

Oke, terima kasih kepada orang-orang yang menunjukkan kemampuan sistem dan CAP_NET_BIND_SERVICEkemampuan. Jika Anda memiliki kernel terbaru, memang mungkin untuk menggunakan ini untuk memulai layanan sebagai port non-root tetapi mengikat rendah. Jawaban singkatnya adalah Anda melakukannya:

setcap 'cap_net_bind_service=+ep' /path/to/program

Dan kemudian kapan saja programdieksekusi setelah itu akan memiliki CAP_NET_BIND_SERVICEkemampuan. setcapada dalam paket debian libcap2-bin.

Sekarang untuk peringatan:

  1. Anda membutuhkan setidaknya kernel 2.6.24
  2. Ini tidak akan berfungsi jika file Anda adalah skrip. (yaitu, menggunakan baris #! untuk meluncurkan penerjemah). Dalam hal ini, sejauh yang saya mengerti, Anda harus menerapkan kapabilitas pada executable interpreter itu sendiri, yang tentu saja merupakan mimpi buruk keamanan, karena setiap program yang menggunakan interpreter itu akan memiliki kapabilitas. Saya tidak dapat menemukan cara bersih dan mudah untuk mengatasi masalah ini.
  3. Linux akan menonaktifkan LD_LIBRARY_PATH pada apa pun programyang memiliki hak istimewa seperti setcapatau suid. Jadi jika Anda programmenggunakan sendiri .../lib/, Anda mungkin harus melihat ke opsi lain seperti penerusan porta.

Sumber:

Catatan: RHEL pertama kali menambahkan ini di v6 .


3
Solusi parsial untuk skrip: buat salinan penerjemah (mis. Bash), berikan kemampuannya tetapi batasi akses ke salinan itu untuk pengguna yang membutuhkannya. Tentu saja, para pengguna itu harus tepercaya, tetapi mereka tetap dapat mengubah skripnya.
Erich Kitzmueller

3
Selain paket debian (biner) yang disebutkan di atas, situs pengembang adalah friedhoff.org/posixfilecaps.html makalah / presentasi / dll yang terkait ...
RandomNickName42

1
pada suse SLES 11.1 saya harus menambahkan kernel param file_caps = 1 ke grub menu.lst agar ini berfungsi.
Shay

2
Ya @ C.Ross karena itu harus diterapkan /usr/bin/javadan kemudian akan membuka kemampuan untuk aplikasi java yang berjalan pada sistem. Kemampuan yang terlalu buruk juga tidak dapat diatur per pengguna.
joeytwiddle

5
Apakah pengaturan setcap bertahan di seluruh reboot; jika tidak apakah ada tempat standar untuk meletakkan aturan ini sehingga dijalankan selama startup sistem? Apakah /etc/security/capability.confdi Debian / Ubuntu ada bantuan?
joeytwiddle

34

Anda dapat melakukan redirect port. Inilah yang saya lakukan untuk server kebijakan Silverlight yang berjalan pada kotak Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

5
Sayangnya ini hanya akan berfungsi untuk koneksi yang diarahkan, yaitu bukan dari mesin lokal.
zbyszek

@ joeytwiddle Saya pikir ada dalam skrip yang menjalankan (dimulai) server Anda, tapi saya mungkin salah. Saya juga ingin tahu: P
Camilo Martin

@CamiloMartin Mencari lagi, dokumentasi Debian yang ditautkan dari pertanyaan merekomendasikan untuk menempatkannya di skrip firewall Anda, atau membuatnya di /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek Ini dapat bekerja untuk koneksi mesin lokal, dengan aturan iptables tambahan: stackoverflow.com/a/31795603/1356953
00500005

Pada kernel lama sepertinya ini tidak didukung untuk IPv6 . Tetapi ternyata itu didukung di ip6tables v1.4.18 dan Linux kernel v3.8.
Craig McQueen

31

Cara standar adalah membuat mereka "setuid" sehingga mereka mulai sebagai root, dan kemudian mereka membuang privilege root itu segera setelah mereka terikat ke port tetapi sebelum mereka mulai menerima koneksi ke sana. Anda dapat melihat contoh yang bagus dari itu dalam kode sumber untuk Apache dan INN. Saya diberitahu bahwa Lighttpd adalah contoh bagus lainnya.

Contoh lain adalah Postfix, yang menggunakan banyak daemon yang berkomunikasi melalui pipa, dan hanya satu atau dua di antaranya (yang sangat sedikit kecuali menerima atau memancarkan byte) dijalankan sebagai root dan sisanya dijalankan pada privilege yang lebih rendah.


1
Menariknya, ini tidak berfungsi di bawah versi Linux terbaru (mungkin hanya Ubuntu) tanpa set CAP_SETUID. Jadi, jika Anda perlu setuid, Anda harus tetap mengatur kemampuan ini.
Mat

Menjatuhkan root privs adalah cara yang tepat untuk melakukan ini. Meskipun tidak perlu mengatur setuid jika Anda memiliki init / pemula / systemd memulai layanan.
Michael Hampton

2
Ini sulit jika program ditulis dalam bahasa yang ditafsirkan atau penerjemah bytecode seperti C # (Mono), Java, Python. (Rupanya Perl telah melakukannya melalui binfmt_miscdan bendera 'C'; Saya tidak yakin tentang yang lain.)
Craig McQueen

sangat kecil kecuali memancarkan byte, itu tidak melakukan sangat sedikit.
Ryan

Jika Anda mengatur setuid biner, itu akan dijalankan sebagai proses root. Namun, pertanyaannya adalah bagaimana mencapainya tanpa menjalankan biner sebagai proses root. Solusi ini adalah jawaban untuk pertanyaan yang berbeda, yaitu: "Bagaimana mungkin pengguna yang tidak memiliki hak pribadi memiliki proses mengikat ke port yang diprivatisasi", tetapi ini bukan pertanyaan yang diajukan, IMHO?
Michael Beer

24

Atau tambal kernel Anda dan hapus centangnya.

(Opsi pilihan terakhir, tidak disarankan).

Di net/ipv4/af_inet.c, hapus dua baris yang bertuliskan

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

dan kernel tidak akan memeriksa port privilege lagi.


22
Ide yang Sangat Buruk karena banyak alasan.
Adam Lassek

24
Ini ide yang buruk. Tetapi sebenarnya akan berhasil. Dan tentu saja membuatku tertawa.
Oto Brglez

23
Tentu saja itu ide yang buruk. Itu sebabnya saya katakan pilihan terakhir. Inti dari open source adalah jika tidak berfungsi seperti yang Anda inginkan, Anda dapat mengubahnya.
Yosua

18
Saya tidak yakin mengapa Anda diturunkan suara. Penulis malware tidak peduli port apa yang mereka dengarkan, dan mereka lebih dari senang untuk membuka port yang lebih besar dari 1024. Tampaknya bagi saya bahwa semua port harus memerlukan privilege, atau tidak ada port yang membutuhkan privilege. Dan membutuhkan root untuk membuka port di bawah 1024 hanya berarti Anda memiliki aplikasi berisiko tinggi yang berjalan sebagai root. Itu sepertinya ide yang sangat bodoh bagi saya. Mungkin saya melewatkan sesuatu di sini ...
jww

9
Kembali pada hari itu, port <1024 digunakan dalam protokol UNIX ke UNIX untuk membuktikan bahwa kode yang berjalan di ujung yang lain berjalan sebagai root. Ini berfungsi cukup baik untuk satu set server UNIX dengan keamanan bersama.
Yosua

22

Anda dapat mengatur terowongan SSH lokal, mis. Jika Anda ingin port 80 mencapai aplikasi Anda terikat 3000:

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Ini memiliki keuntungan bekerja dengan server skrip, dan menjadi sangat sederhana.


1
Favorit pribadi saya. Sangat mudah untuk dihidupkan dan dimatikan dan tidak memerlukan perubahan sistem secara keseluruhan. Ide yang hebat!
Dan Passaro

Ini luar biasa. Sejauh ini jawaban paling sederhana.
michaelsnowden

1
Saya berharap ini cukup mahal dan bukan ide yang baik dalam situasi terikat kinerja, tapi saya tidak bisa memastikan tanpa mengujinya. Enkripsi SSH membutuhkan biaya yang bukan nol.
lahwran

3
Ini bisa dilakukan dengan netcat tanpa overhead ssh:sudo nc -l 80 | nc localhost 3000
Bugster

1
Anda mengenkripsi + mendekripsi hanya untuk memindahkan data ke port lain di mesin yang sama? Juga, ini tidak berfungsi untuk lalu lintas UDP.
Daniel F

19

Pembaruan 2017:

Gunakan authbind


Jauh lebih baik daripada CAP_NET_BIND_SERVICE atau kernel khusus.

  • CAP_NET_BIND_SERVICE memberikan kepercayaan kepada biner tetapi tidak memberikan kontrol atas akses per-port.
  • Authbind memberikan kepercayaan kepada pengguna / grup dan memberikan kontrol atas akses per-port, dan mendukung IPv4 dan IPv6 (dukungan IPv6 telah ditambahkan pada akhir-akhir ini).

    1. Install: apt-get install authbind

    2. Konfigurasikan akses ke port yang relevan, mis. 80 dan 443 untuk 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. Jalankan perintah Anda melalui authbind
      (tentukan secara opsional --deepatau argumen lain, lihat halaman manual):

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

      misalnya

      authbind --deep java -jar SomeServer.jar
      

Sebagai tindak lanjut dari rekomendasi Joshua yang luar biasa (= tidak disarankan kecuali Anda tahu apa yang Anda lakukan) untuk meretas kernel:

Saya pertama kali mempostingnya di sini .

Sederhana. Dengan kernel normal atau lama, Anda tidak.
Seperti yang ditunjukkan oleh yang lain, iptables dapat meneruskan sebuah port.
Seperti yang ditunjukkan oleh yang lain, CAP_NET_BIND_SERVICE juga dapat melakukan pekerjaan itu.
Tentu saja CAP_NET_BIND_SERVICE akan gagal jika Anda meluncurkan program Anda dari skrip, kecuali jika Anda menetapkan batas pada penerjemah shell, yang tidak ada gunanya, Anda bisa menjalankan layanan Anda sebagai root ...
misalnya untuk Java, Anda harus menerapkannya ke JAVA JVM

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Jelas, itu berarti program Java apa pun dapat mengikat port sistem.
Dito untuk mono / .NET.

Saya juga cukup yakin xinetd bukan yang terbaik dari ide.
Tetapi karena kedua metode ini adalah peretasan, mengapa tidak hanya mengangkat batas dengan mengangkat batasan?
Tidak ada yang mengatakan Anda harus menjalankan kernel normal, jadi Anda bisa menjalankan kernel Anda sendiri.

Anda cukup mengunduh sumber untuk kernel terbaru (atau yang sama dengan yang Anda miliki saat ini). Setelah itu, Anda pergi ke:

/usr/src/linux-<version_number>/include/net/sock.h:

Di sana Anda mencari baris ini

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

dan ubah ke

#define PROT_SOCK 0

jika Anda tidak ingin memiliki situasi ssh tidak aman, Anda mengubahnya menjadi ini: #define PROT_SOCK 24

Secara umum, saya akan menggunakan pengaturan terendah yang Anda butuhkan, misalnya 79 untuk http, atau 24 saat menggunakan SMTP pada port 25.

Itu saja.
Kompilasi kernel, dan instal.
Mulai ulang.
Selesai - batas bodoh itu PERLU, dan itu juga berfungsi untuk skrip.

Inilah cara Anda mengompilasi kernel:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

Singkatnya, gunakan iptables jika Anda ingin tetap aman, kompilasi kernel jika Anda ingin memastikan pembatasan ini tidak pernah mengganggu Anda lagi.


Ada alasan untuk downvote? Pembenci root, atau apakah instruksi kompilasi kernel tidak berfungsi?
Stefan Steiger

4
memodifikasi kernel membuat pembaruan di masa depan jauh lebih menyakitkan. saya tidak akan melakukan ini, karena saya tahu saya akan terlalu malas untuk terus memperbarui kernel secara teratur. itu bagus bahwa itu mungkin dalam teori, tetapi itu bukan pilihan yang layak dalam banyak keadaan.
kritzikratzi

Mungkin cara yang lebih aman untuk melakukannya adalah dengan mengganti chmod 777 / etc / authbind / byport / 80 oleh chmod 544 / etc / authbind / byport / 23 dari pada chown login: group / etc / authbind / byport / 23
Jérôme B

18

Kemampuan file tidak ideal, karena mereka dapat rusak setelah pembaruan paket.

Solusi ideal, IMHO, adalah kemampuan untuk membuat shell dengan CAP_NET_BIND_SERVICEset yang dapat diwarisi .

Berikut cara yang agak berbelit-belit untuk melakukan ini:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshutilitas dapat ditemukan dalam paket libcap2-bin di distribusi Debian / Ubuntu. Inilah yang terjadi:

  • sgmengubah ID grup yang efektif menjadi ID pengguna daemon. Ini diperlukan karena capshmembuat GID tidak berubah dan kami jelas tidak menginginkannya.
  • Set bit 'jaga kapabilitas perubahan UID'.
  • Ubah UID menjadi $DAEMONUSER
  • Drops semua topi (pada saat ini semua tutup masih ada karena --keep=1), kecuali diturunkancap_net_bind_service
  • Jalankan perintah Anda ('-' adalah pemisah)

Hasilnya adalah proses dengan pengguna dan grup tertentu, dan cap_net_bind_servicehak istimewa.

Sebagai contoh, baris dari ejabberdskrip startup:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

Dan itu sebenarnya TIDAK bekerja. Tutup tidak disimpan di atas saklar ID pengguna. Ini akan bekerja jika Anda ingin menjalankan sebagai root, tetapi dengan semua kemampuan yang dibatalkan.
Cyberax

Jika saya mencobanya, saya mendapatkan "tidak dapat menjalankan file biner". Bahkan, saya tidak bisa capshmelakukan apa pun selain "tidak dapat mengeksekusi file biner" saat menggunakan opsi --atau ==. Saya ingin tahu apa yang saya lewatkan.
Craig McQueen

@CraigMcQueen, semuanya setelah --diteruskan ke /bin/bash, jadi Anda mungkin ingin mencobanya -c 'your command'. Sayangnya, saya sepertinya mengalami masalah yang sama dengan @Cyberax, karena saya mendapatkan "izin ditolak" ketika mencoba bind.
Amir

Agar ini berfungsi tanpa mengatur kemampuan file (atau menjalankan sebagai root), Anda perlu menggunakan kemampuan sekitar seperti yang dijelaskan dalam jawaban Unix.SE ini . Anda juga bisa menggunakan --gidbukan sg(atau --useryang set --uid, --giddan --groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid

18

Untuk beberapa alasan tidak ada yang menyebutkan tentang menurunkan sysctl net.ipv4.ip_unprivileged_port_start dengan nilai yang Anda butuhkan. Contoh: Kita perlu mengikat aplikasi kita ke port 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Beberapa orang mungkin mengatakan, ada masalah keamanan potensial: pengguna yang tidak memiliki hak sekarang dapat mengikat ke port istimewa lainnya (444-1024). Tetapi Anda dapat mengatasi masalah ini dengan mudah dengan iptables, dengan memblokir port lain:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

Perbandingan dengan metode lain. Metode ini:

  • dari beberapa titik adalah (IMO) bahkan lebih aman daripada pengaturan CAP_NET_BIND_SERVICE / setuid, karena aplikasi tidak setuid sama sekali, bahkan sebagian (kemampuan sebenarnya). Sebagai contoh, untuk mengetahui jumlah aplikasi yang diaktifkan dengan kapabilitas, Anda perlu mengubah sysctl fs.suid_dumpable (yang mengarah ke masalah keamanan potensial lainnya) Juga, ketika CAP / suid diatur, direktori / proc / PID dimiliki oleh root, jadi pengguna non-root Anda tidak akan memiliki informasi penuh / kontrol proses yang sedang berjalan, misalnya, pengguna tidak akan dapat (dalam kasus umum) untuk menentukan koneksi mana yang dimiliki aplikasi melalui / proc / PID / fd / (netstat -aptn | grep PID).
  • memiliki kelemahan keamanan: sementara aplikasi Anda (atau aplikasi apa pun yang menggunakan port 443-1024) tidak berfungsi karena beberapa alasan, aplikasi lain dapat mengambil port tersebut. Tetapi masalah ini juga dapat diterapkan pada CAP / suid (jika Anda mengaturnya pada juru bahasa, misalnya java / nodejs) dan iptables-redirect. Gunakan metode soket sistem untuk mengecualikan masalah ini. Gunakan metode authbind untuk hanya mengizinkan pengikatan pengguna khusus.
  • tidak memerlukan pengaturan CAP / suid setiap kali Anda menggunakan versi aplikasi baru.
  • tidak memerlukan dukungan / modifikasi aplikasi, seperti metode soket sistem.
  • tidak memerlukan kernel rebuild (jika menjalankan versi mendukung pengaturan sysctl ini)
  • tidak melakukan LD_PRELOAD seperti metode authbind / privbind, ini berpotensi memengaruhi kinerja, keamanan, perilaku (bukan? belum diuji). Dalam sisanya authbind adalah metode yang sangat fleksibel dan aman.
  • performa tinggi metode iptables REDIRECT / DNAT, karena tidak memerlukan terjemahan alamat, pelacakan status koneksi, dll. Ini hanya terlihat pada sistem beban tinggi.

Tergantung pada situasinya, saya akan memilih antara sysctl, CAP, authbind dan iptables-redirect. Dan ini luar biasa karena kami memiliki begitu banyak pilihan.


2
Terima kasih atas jawaban Anda! Tampaknya fungsi ini pertama kali muncul di Linux 4.11 pada April 2017 , jadi tidak ada pada tahun 2009 ketika saya pertama kali mengajukan pertanyaan ini. Saya juga melakukan tes cepat, dan tampaknya juga berfungsi untuk IPV6, meskipun "ipv4" ada dalam nama sysctl.
Jason Creighton

15

Dua kemungkinan sederhana lainnya:

Ada solusi lama (tidak modis) untuk "a daemon yang mengikat port rendah dan kontrol tangan ke daemon Anda". Ini disebut inetd (atau xinetd). Kontra adalah:

  • daemon Anda perlu berbicara di stdin / stdout (jika Anda tidak mengontrol daemon - jika Anda tidak memiliki sumber - maka ini mungkin showstopper, meskipun beberapa layanan mungkin memiliki flag kompatibilitas-inetd)
  • proses daemon baru bercabang untuk setiap koneksi
  • itu satu tautan ekstra dalam rantai

Pro:

  • tersedia di UNIX lama
  • sekali sysadmin Anda telah mengatur konfigurasi, Anda baik untuk melanjutkan pengembangan Anda (ketika Anda membangun kembali daemon Anda, mungkin Anda kehilangan kemampuan setcap? Dan kemudian Anda harus kembali ke admin Anda "tolong pak .. . ")
  • daemon tidak perlu khawatir tentang hal-hal jaringan itu, cukup bicara di stdin / stdout
  • dapat mengkonfigurasi untuk mengeksekusi daemon Anda sebagai pengguna non-root, seperti yang diminta

Alternatif lain: proxy yang diretas (netcat atau bahkan sesuatu yang lebih kuat ) dari port istimewa ke beberapa port bernomor tinggi sewenang-wenang di mana Anda dapat menjalankan daemon target Anda. (Netcat jelas bukan solusi produksi, tetapi "hanya kotak dev saya", kan?). Dengan cara ini Anda dapat terus menggunakan versi server yang mampu jaringan, hanya perlu root / sudo untuk memulai proxy (saat boot), tidak akan mengandalkan kapabilitas yang kompleks / berpotensi rapuh.


1
Saran yang bagus Untuk beberapa alasan saya bahkan tidak berpikir untuk menggunakan inetd. Kecuali bahwa layanan yang dimaksud adalah berbasis UDP, sehingga sedikit lebih rumit daripada layanan TCP. Saya punya solusi yang bekerja saat ini dengan setcap, tetapi saya harus memikirkan ini.
Jason Creighton

Untuk opsi kedua, Anda bahkan dapat memulai proksi menggunakan inetd ... (Tetapi IPTables kemungkinan biaya overhead yang lebih rendah ...)
Gert van den Berg

14

"Pemecahan masalah standar" saya menggunakan socat sebagai pengalih ruang-pengguna:

socat tcp6-listen:80,fork tcp6:8080

Berhati-hatilah karena ini tidak akan menjadi masalah, forking itu mahal tapi itu cara socat bekerja.


13

Linux mendukung kemampuan untuk mendukung izin yang lebih halus daripada hanya "aplikasi ini dijalankan sebagai root". Salah satu kemampuan itu CAP_NET_BIND_SERVICEadalah tentang mengikat ke port istimewa (<1024).

Sayangnya saya tidak tahu bagaimana cara mengeksploitasi itu untuk menjalankan aplikasi sebagai non-root saat masih memberikannya CAP_NET_BIND_SERVICE(mungkin menggunakan setcap, tetapi pasti ada solusi yang ada untuk ini).


Tidak akan berfungsi untuk skrip, atau program JVM / mono individual.
Stefan Steiger

13

Saya tahu ini adalah pertanyaan lama, tetapi sekarang dengan kernel terbaru (> = 4.3) akhirnya ada jawaban yang bagus untuk ini - kemampuan sekitar.

Jawaban cepatnya adalah mengambil salinan libcap versi terbaru (yang belum dirilis) dari git dan mengompilasinya. Salin progs/capshbiner yang dihasilkan di suatu tempat ( /usr/local/binmerupakan pilihan yang baik). Kemudian, sebagai root, mulailah program Anda dengan

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

Agar, kita

  • Menyatakan bahwa ketika kami beralih pengguna, kami ingin menjaga set kemampuan kami saat ini
  • Mengalihkan pengguna & grup ke 'nama-layanan-pengguna Anda'
  • Menambahkan cap_net_bind_servicekemampuan ke set yang diwarisi & ambient
  • Forking bash -c 'your-command'(karena capshsecara otomatis memulai bash dengan argumen setelah --)

Ada banyak hal yang terjadi di bawah tenda di sini.

Pertama, kami menjalankan sebagai root, jadi secara default, kami mendapatkan set lengkap kemampuan. Termasuk dalam ini adalah kemampuan untuk beralih uid & gid dengan setuiddan setgidsyscalls. Namun, biasanya ketika sebuah program melakukan ini, ia kehilangan set kemampuannya - ini adalah cara lama menjatuhkan root dengan setuidmasih bekerja. The --keep=1bendera mengatakan capshuntuk mengeluarkan prctl(PR_SET_KEEPCAPS)syscall, yang menonaktifkan menjatuhkan kemampuan ketika mengubah pengguna. Perubahan pengguna yang sebenarnya capshterjadi dengan --userflag, yang berjalan setuiddan setgid.

Masalah berikutnya yang perlu kita selesaikan adalah bagaimana mengatur kemampuan dengan cara yang dijalankan setelah kita execmenjadi anak-anak kita. Sistem kapabilitas selalu memiliki seperangkat kapabilitas 'yang diwarisi', yang merupakan "seperangkat kapabilitas yang dipertahankan di seluruh execve (2)" [ kapabilitas (7) ]. Sementara ini terdengar seperti itu memecahkan masalah kami (hanya mengatur cap_net_bind_servicekemampuan untuk diwarisi, kan?), Ini sebenarnya hanya berlaku untuk proses istimewa - dan proses kami tidak istimewa lagi, karena kami sudah mengubah pengguna (dengan --userbendera).

Set kemampuan ambient baru mengatasi masalah ini - itu adalah "seperangkat kemampuan yang dipertahankan di seluruh (2) program yang tidak diistimewakan." Dengan meletakkan cap_net_bind_servicedi ambient set, ketika capshexec adalah program server kami, program kami akan mewarisi kemampuan ini dan dapat mengikat pendengar ke port rendah.

Jika Anda tertarik untuk mempelajari lebih lanjut, halaman manual kemampuan menjelaskan ini dengan sangat rinci. Menjalankan capshmelalui stracejuga sangat informatif!


The --addambbendera diperkenalkan dengan libcap 2,26. Sistem saya memiliki 2,25 dan saya harus membangun dari sumber.
ngreen

12

TLDR: Untuk "jawabannya" (seperti yang saya lihat), lompat ke bagian >> TLDR << dalam jawaban ini.

OK, saya sudah menemukan jawabannya (untuk yang sebenarnya kali ini), jawaban untuk pertanyaan ini, dan jawaban saya ini juga merupakan cara meminta maaf karena mempromosikan jawaban lain (baik di sini maupun di twitter) yang saya pikir adalah "yang terbaik ", tetapi setelah mencobanya, ternyata saya salah tentang itu. Belajarlah dari kesalahan anak-anak saya: jangan mempromosikan sesuatu sampai Anda benar-benar mencobanya sendiri!

Sekali lagi, saya meninjau semua jawaban di sini. Saya sudah mencoba beberapa dari mereka (dan memilih untuk tidak mencoba yang lain karena saya tidak suka solusinya). Saya berpikir bahwa solusinya adalah menggunakan systemddengan yang Capabilities=dan CapabilitiesBindingSet=pengaturan. Setelah bergulat dengan ini selama beberapa waktu, saya menemukan bahwa ini bukan solusi karena:

Kemampuan dimaksudkan untuk membatasi proses root!

Seperti yang dikatakan OP dengan bijak, selalu yang terbaik untuk menghindari itu (untuk semua dasmon Anda jika memungkinkan!).

Anda tidak dapat menggunakan opsi terkait Kemampuan dengan User=dan Group=dalam systemdfile unit, karena kemampuan SELALU diatur ulang ketika execev(atau apa pun fungsinya) dipanggil. Dengan kata lain, saat systemdfork dan drop permsnya, kapabilitas direset. Tidak ada jalan lain untuk hal ini, dan semua logika yang mengikat dalam kernel adalah dasar di sekitar uid = 0, bukan kemampuan. Ini berarti bahwa kecil kemungkinan Kapabilitas akan menjadi jawaban yang tepat untuk pertanyaan ini (setidaknya dalam waktu dekat). Kebetulan, setcapseperti yang disebutkan orang lain, bukanlah solusi. Itu tidak berfungsi untuk saya, tidak berfungsi dengan baik dengan skrip, dan itu akan diatur ulang kapan pun file berubah.

Dalam pertahanan saya yang sedikit, saya menyatakan (dalam komentar yang sekarang saya hapus), bahwa saran iptables James (yang juga disebutkan oleh OP), adalah "solusi terbaik ke-2". :-P

>> TLDR <<

Solusinya adalah menggabungkan systemddengan iptablesperintah on-the-fly , seperti ini ( diambil dari DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Di sini kita mencapai yang berikut:

  • Daemon mendengarkan pada 5333, tetapi koneksi berhasil diterima pada 53 berkat iptables
  • Kami dapat memasukkan perintah dalam file unit itu sendiri, dan dengan demikian kami menyelamatkan orang-orang sakit kepala. systemdmembersihkan aturan firewall untuk kami, pastikan untuk menghapusnya ketika daemon tidak berjalan.
  • Kami tidak pernah berjalan sebagai root, dan kami membuat eskalasi hak istimewa tidak mungkin (setidaknya systemddiklaim), konon bahkan jika daemon dikompromikan dan ditetapkan uid=0.

iptablesmasih, sayangnya, utilitas yang cukup jelek dan sulit digunakan. Jika daemon sedang mendengarkan eth0:0alih-alih eth0, misalnya, perintahnya sedikit berbeda .


2
Tidak ada yang harus menggunakan alias gaya lama seperti eth0:0lagi kecuali mereka memiliki distribusi Linux yang sangat kuno . Mereka telah ditinggalkan selama bertahun-tahun dan pada akhirnya akan pergi.
Michael Hampton

1
Saya pikir maksud Anda OpenVZ. (SolusVM adalah panel kontrol.) Dan ya, OpenVZ melakukan banyak hal yang salah, jaringan hanya menjadi salah satunya.
Michael Hampton

1
Nah, maksud saya dengan SolusVM. Dari / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

1
Masih bukan OpenVZ ... Setidaknya saya tidak menggunakannya, juga VPS saya.
Greg Slepak

7
Terima kasih telah menunjukkan fitur kemampuan systemd. Namun tidak perlu untuk iptables yang kompleks ketika systemd memulai biner secara langsung (bukan skrip). Pengaturan AmbientCapabilities=CAP_NET_BIND_SERVICEbekerja dengan sangat baik untuk saya dalam kombinasi dengan User=.
Ruud

11

systemd adalah pengganti sysvinit yang memiliki opsi untuk meluncurkan daemon dengan kemampuan spesifik. Options Capabilities =, CapabilityBoundingSet = di manual systemd.exec (5) .


2
Saya sebelumnya merekomendasikan jawaban ini, tetapi setelah mencobanya, sekarang saya tidak melakukannya. Lihat jawaban saya untuk alternatif yang masih digunakan systemd.
Greg Slepak

10

Port redirect paling masuk akal bagi kami, tetapi kami mengalami masalah di mana aplikasi kami akan menyelesaikan url secara lokal yang juga perlu dirutekan ulang; (itu berarti Anda heboh ).

Ini juga akan memungkinkan Anda untuk dialihkan ketika mengakses url pada mesin lokal.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080

9

Linux modern mendukung /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Dengan systemd, Anda hanya perlu sedikit memodifikasi layanan Anda untuk menerima soket yang sudah diaktifkan.

Anda nantinya dapat menggunakan systemd socket activ .

Tidak diperlukan kemampuan, iptables atau trik lain.

Ini adalah konten file systemd yang relevan dari contoh server http python sederhana ini

Mengajukan httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

Mengajukan httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Saat memulai:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Kemudian Anda dapat mengikat ke port yang Anda tuju.


apakah --to-portada man iptableshanya menyebutkan --to-ports(jamak).
Abdull

ive memperhatikan beberapa perbedaan dan saya telah melompat
James

Lihat jawaban ini untuk cara menggabungkan ini dengan systemd.
Greg Slepak


3

Ada juga 'djb way'. Anda dapat menggunakan metode ini untuk memulai proses Anda sebagai root berjalan pada port apa pun di bawah tcpserver, maka itu akan menyerahkan kontrol proses kepada pengguna yang Anda tentukan segera setelah proses dimulai.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Untuk info lebih lanjut, lihat: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Karena OP hanya pengembangan / pengujian, solusi yang kurang dari ramping mungkin membantu:

setcap dapat digunakan pada juru bahasa skrip untuk memberikan kemampuan pada skrip. Jika setcaps pada biner interpreter global tidak dapat diterima, buat salinan biner lokal (setiap pengguna dapat) dan dapatkan root untuk setcap pada salinan ini. Python2 (setidaknya) berfungsi dengan baik dengan salinan lokal juru bahasa di pohon pengembangan skrip Anda. Suid tidak diperlukan sehingga pengguna root dapat mengontrol kemampuan apa yang dapat diakses oleh pengguna.

Jika Anda perlu melacak pembaruan sistem ke penerjemah, gunakan skrip shell seperti berikut untuk menjalankan skrip Anda:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

1

Saya mencoba metode iptables PREROUTING REDIRECT. Pada kernel yang lebih tua, sepertinya jenis aturan ini tidak didukung untuk IPv6 . Tetapi ternyata sekarang didukung di ip6tables v1.4.18 dan Linux kernel v3.8.

Saya juga menemukan bahwa PREROUTING REDIRECT tidak berfungsi untuk koneksi yang dimulai di dalam mesin. Untuk bekerja untuk koneksi dari mesin lokal, tambahkan aturan OUTPUT juga - lihat pengalihan port iptables yang tidak berfungsi untuk localhost . Misalnya sesuatu seperti:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Saya juga menemukan bahwa PREROUTING REDIRECT juga memengaruhi paket yang diteruskan . Yaitu, jika mesin juga meneruskan paket antar antarmuka (mis. Jika itu bertindak sebagai titik akses Wi-Fi yang terhubung ke jaringan Ethernet), maka aturan iptables juga akan menangkap koneksi klien yang terhubung ke tujuan Internet, dan mengarahkan mereka ke mesin. Bukan itu yang saya inginkan — saya hanya ingin mengarahkan kembali koneksi yang diarahkan ke mesin itu sendiri. Saya menemukan saya bisa membuatnya hanya mempengaruhi paket yang ditujukan ke kotak, dengan menambahkan -m addrtype --dst-type LOCAL. Misalnya sesuatu seperti:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Satu kemungkinan lainnya adalah menggunakan penerusan port TCP. Misalnya menggunakan socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

Namun satu kelemahan dengan metode itu adalah, aplikasi yang mendengarkan pada port 8080 kemudian tidak tahu alamat sumber koneksi yang masuk (misalnya untuk logging atau keperluan identifikasi lainnya).


0

Jawab pada 2015 / Sep:

ip6tables sekarang mendukung IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Anda membutuhkan kernel 3.7+

Bukti:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.