Kök dışı işlemlerin Linux'ta "ayrıcalıklı" bağlantı noktalarına bağlanmasının bir yolu var mı?


389

Benden başka kullanıcı olmayacaksa, geliştirme kutumda bu sınırlamanın olması çok can sıkıcı bir durum.

Standart geçici çözümlerin farkındayım , ancak hiçbiri tam olarak istediğimi yapmıyor:

  1. authbind (Debian test, 1.0 sürümü yalnızca IPv4'ü destekler)
  2. Düşük bir bağlantı noktasını yüksek bir bağlantı noktasına yeniden yönlendirmek için iptables REDIRECT hedefini kullanma ("nat" tablosu henüz iptables için değil, iptables IPv6 sürümü)
  3. sudo (Kök olarak koşmak kaçınmaya çalıştığım şey)
  4. SELinux (veya benzeri). (Bu sadece geliştirici kutum, fazladan karmaşıklık getirmek istemiyorum.)

sysctlKök dışı işlemlerin Linux'ta "ayrıcalıklı" bağlantı noktalarına (1024'ten küçük bağlantı noktaları) bağlanmasına izin vermek için basit bir değişken var mı , yoksa sadece şansım yok mu?

EDIT: Bazı durumlarda bunu yapmak için yetenekleri kullanabilirsiniz .


5
Deneyimlerime göre, bunu denemenin bir nedeni Apache (veya lighttpd) kullanmak yerine bir web sunucusu yazmaktır.
S.Lott


15
Çünkü bu örnekte, IPv6 kullanıyordum, bu yüzden "olağan" geçici çözümlerden bazıları (authbind ve iptables REDIRECT) benim için çalışmadı.
Jason Creighton


1
Bunu yapmanın birkaç yolu var. Bkz. Kök dışı işlemin bağlantı noktası 80 ve 443'e bağlanmasına izin verilsin mi? Süper Kullanıcı.
jww

Yanıtlar:


392

Tamam, yetenekler sistemini ve CAP_NET_BIND_SERVICEkabiliyetini belirten insanlar sayesinde . Eğer yakın zamanda bir çekirdeğiniz varsa, bunu bir hizmeti kök olmayan olarak başlatmak, ancak düşük bağlantı noktalarını bağlamak için kullanmak mümkündür. Kısa cevap şudur:

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

Ve daha sonra herhangi bir programzamanda yürütülür CAP_NET_BIND_SERVICE. setcapdebian paketinde libcap2-bin.

Şimdi uyarılar için:

  1. En az 2.6.24 çekirdeğe ihtiyacınız olacak
  2. Dosyanız bir komut dosyasıysa bu çalışmaz. (örneğin, bir tercüman başlatmak için #! satırı kullanır). Bu durumda, anladığım kadarıyla, söz konusu tercümanı kullanan herhangi bir programın kabiliyetine sahip olacağı için, yeteneği elbette bir güvenlik kabusu olan tercüman yürütülebilirine uygulamak zorunda kalacaksınız. Bu soruna geçici bir çözüm bulmak için temiz ve kolay bir yol bulamadım.
  3. Linux, veya programgibi yüksek ayrıcalıklara sahip olanlarda LD_LIBRARY_PATH özelliğini devre dışı bırakır . Dolayısıyla, kendinizinkini kullanıyorsa, port yönlendirme gibi başka bir seçeneğe bakmanız gerekebilir.setcapsuidprogram.../lib/

Kaynaklar:

Not: RHEL bunu önce v6'ya ekledi .


3
Komut dosyaları için kısmi geçici çözüm: yorumlayıcının bir kopyasını (örn. Bash) oluşturun, ona yetenekler verin, ancak kopyaya ihtiyacı olan kullanıcılara erişimi kısıtlayın. Tabii ki, bu kullanıcıların güvenilir olması gerekir, ancak komut dosyasını yine de değiştirebilirler.
Erich Kitzmueller

3
Yukarıda belirtilen debian (ikili) paketinin yanı sıra, geliştiricinin sitesi friedhoff.org/posixfilecaps.html ilişkili belgeler / sunumlar / vb ...
RandomNickName42

1
suse SLES 11.1 üzerinde çalışmak için menu.lst çek param file_caps = 1 eklemek zorunda kaldı.
Shay

2
Evet /usr/bin/java. Kullanıcı başına çok kötü özellikler de ayarlanamaz.
joeytwiddle

5
Setcap ayarı yeniden başlatmalarda devam eder mi? değilse, bu kuralı sistem başlangıcı sırasında çalıştırılacak standart bir yer var mı? Mı /etc/security/capability.confherhangi bir yardım Debian / Ubuntu üzerinde?
joeytwiddle

34

Bir bağlantı noktası yönlendirmesi yapabilirsiniz. Linux kutusunda çalışan bir Silverlight ilke sunucusu için yaptığım şey bu

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

5
Ne yazık ki bu sadece yönlendirilmiş bağlantılar için çalışacaktır, yani yerel makineden değil.
zbyszek

@joeytwiddle Sanırım sunucunuzu çalıştıran (başlatan) komut dosyasında, ama yanlış olabilir. Ayrıca bilmek istiyorum: P
Camilo Martin

@CamiloMartin Tekrar bakıldığında , sorudan bağlanan Debian belgeleri güvenlik duvarı betiğinize yerleştirmenizi veya adresinden bir tane oluşturmanızı önerir /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek Bu, yerel bir makine bağlantıları için ek iptables kuralıyla
çalışabilir

Eski çekirdeklerde bunun IPv6 için desteklenmediği görülüyor . Ama görünüşe göre ip6tables v1.4.18 ve Linux çekirdeği v3.8'de destekleniyor.
Craig McQueen

31

Standart yol onları "setuid" yapmaktır, böylece kök olarak başlarlar ve daha sonra limana bağlanır bağlanmaz, ancak bağlantıyı kabul etmeye başlamadan önce bu kök ayrıcalığını atarlar. Bunun iyi örneklerini Apache ve INN kaynak kodunda görebilirsiniz. Bana Lighttpd'nin başka bir iyi örnek olduğu söylendi.

Başka bir örnek, borular yoluyla iletişim kuran birden çok cinsten yararlanan Postfix'tir ve bunlardan yalnızca biri veya ikisi (kabul veya bayt yaymak dışında çok az şey yapar) kök olarak çalışır ve geri kalanı daha düşük bir ayrıcalıkla çalışır.


1
İlginçtir, bu CAP_SETUID ayarlanmadan Linux'un son sürümlerinde (belki sadece Ubuntu) çalışmaz. Setuid'e ihtiyacınız varsa, bu özelliği yine de ayarlamanız gerekir.
Matt

Kök gizleri bırakmak, bunu yapmanın doğru yoludur. İnit / upstart / systemd hizmetini başlattıysanız setuid'i ayarlamak gerekli olmasa da.
Michael Hampton

2
Program, yorumlanmış bir dilde veya C # (Mono), Java, Python gibi bayt kod yorumlayıcısında yazılıyorsa bu zordur. (Görünüşe göre Perl bunu binfmt_miscve 'C' bayrağını yaptı; diğerleri hakkında emin değilim.)
Craig McQueen

emit baytları dışında çok az, bu çok az bir şey yapmıyor.
Ryan

İkili bir setuid ayarlarsanız, kök işlemi olarak çalışır. Ancak soru, ikili işleminizi kök işlemi olarak çalıştırmadan bunu nasıl başaracağınızı sorar. Bu çözüm farklı bir sorunun cevabıdır: "Ayrıcalıklı olmayan bir kullanıcı ayrıcalıklı bir bağlantı noktasına nasıl bağlanabilir?", Ancak bu sorulan soru değil, IMHO?
Michael Beer

24

Ya da çekirdeğinizi yamalayın ve onay işaretini kaldırın.

(Son çare seçeneği, önerilmez).

Bölümünde net/ipv4/af_inet.c, okunan iki satırı kaldırın.

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

ve çekirdek artık ayrıcalıklı bağlantı noktalarını denetlemeyecek.


22
Birçok nedenden dolayı çok kötü bir fikir.
Adam Lassek

24
Bu kötü bir fikir. Ama aslında işe yarar. Ve kesinlikle beni güldürdü.
Oto Brglez

23
Tabii ki bu kötü bir fikir. Bu yüzden son çare dedim. Açık kaynak noktası, istediğiniz şekilde çalışmazsa, onu değiştirebilirsiniz.
Joshua

18
Neden aşağı indirildiğinden emin değilim. Kötü amaçlı yazılım yazarları hangi bağlantı noktasını dinlediklerini umursamazlar ve 1024'ten büyük bir bağlantı noktası açmaktan mutluluk duyarlar. Ve 1024'ün altında bir bağlantı noktası açmak için root istemek, root olarak çalışan yüksek riskli bir uygulamanız olduğu anlamına gelir. Bu benim için gerçekten aptalca bir fikir gibi görünüyor . Belki burada bir şey eksik ...
jww

9
Gün içinde, diğer uçta çalışan kodun root olarak çalıştığını kanıtlamak için UNIX to UNIX protokollerinde <1024 bağlantı noktası kullanıldı. Bu, ortak güvenliği olan bir dizi UNIX sunucusu için oldukça iyi işledi.
Joshua

22

Yerel bir SSH tüneli oluşturabilirsiniz, örneğin 80 numaralı bağlantı noktasının uygulamanıza 3000 değerine çarpmasını istiyorsanız:

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

Bu, komut dosyası sunucularıyla çalışma ve çok basit olma avantajına sahiptir.


1
Benim kişisel favorim. Açılması ve kapatılması çok kolaydır ve sistem genelinde değişiklik gerektirmez. İyi fikir!
Dan Passaro

Bu harika. Şimdiye kadar en basit cevap.
michaelsnowden

1
Bunun orta derecede pahalı olmasını ve herhangi bir performansa bağlı durumda iyi bir fikir olmasını beklemezdim, ancak test etmeden emin olamıyorum. SSH şifrelemesi sıfır olmayan bir tutara mal olur.
lahwran

3
Bu ssh yükü olmadan netcat ile yapılabilir:sudo nc -l 80 | nc localhost 3000
Bugster

1
Yalnızca aynı makinedeki başka bir bağlantı noktasına veri taşımak için şifreliyor musunuz? Ayrıca, bu UDP trafiği için çalışmaz.
Daniel F

19

Güncelleme 2017:

kullanım Yetkilendirmeyi


CAP_NET_BIND_SERVICE veya özel bir çekirdekten çok daha iyi.

  • CAP_NET_BIND_SERVICE ikili dosyaya güven verir, ancak bağlantı noktası başına erişim üzerinde denetim sağlamaz.
  • Authbind, kullanıcıya / gruba güven verir ve bağlantı noktası başına erişim üzerinde denetim sağlar ve hem IPv4 hem de IPv6'yı destekler (IPv6 desteği geç saatlerde eklenmiştir).

    1. Yüklemek: apt-get install authbind

    2. İlgili bağlantı noktalarına erişimi yapılandırın, örneğin tüm kullanıcılar ve gruplar için 80 ve 443:

      sudo dokunma / 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. Komutunuzu şu yolla gerçekleştirin: authbind
      (isteğe bağlı olarak --deepveya diğer bağımsız değişkenler, kılavuz sayfasına bakın):

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

      Örneğin

      authbind --deep java -jar SomeServer.jar
      

Joshua'nın muhteşem (= ne yaptığını bilmiyorsan tavsiye edilmez) için bir çekirdek olarak tavsiye:

İlk olarak buraya gönderdim .

Basit. Normal veya eski bir çekirdeğe sahip değilsiniz.
Başkaları tarafından işaret edildiği gibi, iptables bir limanı iletebilir.
Başkaları tarafından da belirtildiği gibi, CAP_NET_BIND_SERVICE işi ​​de yapabilir.
Tabii ki programınızı bir komut dosyasından başlatırsanız CAP_NET_BIND_SERVICE başarısız olur, kabuk yorumlayıcısının kapağını anlamsız olarak ayarlamazsanız, hizmetinizi kök olarak da çalıştırabilirsiniz ...
örneğin Java için, uygulamanız gerekir JAVA JVM'ye

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

Açıkçası, bu, herhangi bir Java programının sistem bağlantı noktalarını bağlayabileceği anlamına gelir.
Mono / .NET için Dito.

Ayrıca xinetd'in en iyi fikir olmadığından da eminim.
Ancak her iki yöntem de hack olduğundan, neden sadece kısıtlamayı kaldırarak sınırı kaldırmıyorsunuz?
Kimse normal bir çekirdeği çalıştırmanız gerektiğini söylemedi, böylece sadece kendi çekirdeğinizi çalıştırabilirsiniz.

En son çekirdeğin (veya şu anda sahip olduğunuz) için kaynağı indirmeniz yeterlidir. Daha sonra şuraya gidersiniz:

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

Orada bu çizgiye bakıyorsun

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

ve olarak değiştir

#define PROT_SOCK 0

güvensiz bir ssh durumu yaşamak istemiyorsanız, bunu şu şekilde değiştirirsiniz: #define PROT_SOCK 24

Genellikle, ihtiyacınız olan en düşük ayarı kullanırdım, örneğin http için 79 veya 25 numaralı bağlantı noktasında SMTP kullanırken 24.

Hepsi bu kadar.
Çekirdeği derleyin ve yükleyin.
Yeniden Başlatma.
Tamamlandı - bu aptal sınır GONE'dir ve bu da komut dosyaları için de çalışır.

Bir çekirdeği şöyle derlersiniz:

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 ..

Özetle, güvenli kalmak istiyorsanız iptables kullanın, bu kısıtlamanın sizi bir daha asla rahatsız etmediğinden emin olmak istiyorsanız çekirdeği derleyin.


Downvote için herhangi bir neden var mı? Kök düşmanı veya çekirdek derleme talimatları çalışmadı mı?
Stefan Steiger

4
çekirdeği değiştirmek gelecekteki güncellemeleri çok daha acı verici hale getirir. Bunu yapmazdım, çünkü düzenli olarak çekirdeğimi güncellemeye devam etmek için çok tembel olacağını biliyorum. teoride mümkün olması güzel, ancak birçok durumda geçerli bir seçenek değil.
kritzikratzi

Belki de bunu yapmanın daha güvenli bir yolu chmod 777 / etc / authbind / byport / 80'i chmod 544 / etc / authbind / byport / 23 ile chown login: group / etc / authbind / byport / 23
Jérôme B

18

Dosya yetenekleri ideal değildir, çünkü paket güncellemesinden sonra bozulabilirler.

İdeal çözüm olan IMHO, kalıtsal bir kabuk oluşturma yeteneği olmalıdır CAP_NET_BIND_SERVICE set .

İşte bunu yapmanın biraz karmaşık bir yolu:

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

capshyardımcı programı Debian / Ubuntu dağıtımlarındaki libcap2-bin paketinde bulunabilir. Neler oluyor:

  • sgetkin grup kimliğini daemon kullanıcısınınkine değiştirir. Bu gereklidir, çünkü capshGID'yi değiştirmeden bırakır ve kesinlikle istemiyoruz.
  • Bit 'UID değişikliğinde tutma yeteneklerini' ayarlar.
  • UID'yi olarak değiştirir $DAEMONUSER
  • --keep=1Kalıtım hariç, tüm kapakları düşürür (şu anda tüm kapaklar hala mevcut )cap_net_bind_service
  • Komutunuzu yürütür ('-' bir ayırıcıdır)

Sonuç, belirtilen kullanıcı ve grup ve cap_net_bind_serviceayrıcalıklara sahip bir işlemdir .

Örnek olarak, ejabberdbaşlangıç ​​komut dosyasından bir satır :

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

Ve aslında işe yaramaz. Caps kullanıcı kimliği anahtarı üzerinde korunmaz. Kök olarak çalıştırmak istiyorsanız, ancak tüm yetenekler düştüğünde çalışır.
Cyberax

Bunu denerseniz, "ikili dosya yürütülemiyor" alırım. Aslında, alamayan capshyapmak şeyi kullanırken "ikili dosyayı yürütemiyor" dışında --veya ==seçenekleri. Neyi kaçırdığımı merak ediyorum.
Craig McQueen

@CraigMcQueen, sonraki her şey --aktarılır /bin/bash, bu yüzden denemek isteyebilirsiniz -c 'your command'. Ne yazık ki, @Cyberax ile aynı sorunu yaşıyor gibi görünüyor, çünkü denerken "izin reddedildi" bind.
Amir

Bunun dosya özelliklerini ayarlamadan (veya kök olarak çalışmadan) çalışması için , bu Unix.SE yanıtında açıklandığı gibi ortam özelliklerini kullanmanız gerekir . Ayrıca kullanabilirsiniz yerine (veya hangi setleri , ve ): --gidsg--user--uid--gid--groupssudo 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

Nedense hiç kimse sysctl net.ipv4.ip_unprivileged_port_start öğesini ihtiyacınız olan değere düşürmekten bahsetmez. Örnek: Uygulamamızı 443 bağlantı noktasına bağlamamız gerekiyor.

sysctl net.ipv4.ip_unprivileged_port_start=443

Bazıları, potansiyel bir güvenlik sorunu olduğunu söyleyebilir: şimdi ayrıcalıksız kullanıcılar diğer ayrıcalıklı bağlantı noktalarına bağlanabilir (444-1024). Ancak diğer bağlantı noktalarını engelleyerek iptables ile bu sorunu kolayca çözebilirsiniz:

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

Diğer yöntemlerle karşılaştırma. Bu method:

  • bir noktadan itibaren (IMO) CAP_NET_BIND_SERVICE / setuid ayarından bile daha güvenlidir, çünkü bir uygulama kısmen de olsa ayarlanmış değildir (yetenekler aslında). Örneğin, yetenek özellikli bir uygulama kümesini yakalamak için sysctl fs.suid_dumpable'ı (başka bir potansiyel güvenlik sorununa yol açar) değiştirmeniz gerekir. Ayrıca, CAP / suid ayarlandığında / proc / PID dizini köke aittir, bu nedenle root olmayan kullanıcınız çalışan işlemin tam bilgisine / kontrolüne sahip olmayacaktır, örneğin, kullanıcı / proc / PID / fd / (netstat -aptn | grep üzerinden uygulamaya hangi bağlantıların ait olduğunu belirleyemez (yaygın olarak). PID).
  • güvenlik dezavantajı vardır: Uygulamanız (veya 443-1024 bağlantı noktalarını kullanan herhangi bir uygulama) bir nedenden dolayı devre dışı kalırken, başka bir uygulama bağlantı noktasını alabilir. Ancak bu sorun, CAP / suid'e (örneğin, java / nodejs yorumlayıcı olarak ayarlamanız durumunda) ve iptables-redirect'e de uygulanabilir. Bu sorunu dışlamak için systemd-socket yöntemini kullanın. Yalnızca özel kullanıcı bağlanmasına izin vermek için authbind yöntemini kullanın.
  • uygulamanın her yeni sürümünü dağıttığınızda CAP / suid ayarını yapmanız gerekmez.
  • systemd-socket yöntemi gibi uygulama desteği / modifikasyonu gerektirmez.
  • çekirdek yeniden oluşturma gerektirmez (çalışan sürüm bu sysctl ayarını destekliyorsa)
  • LD_PRELOAD authbind / privbind yöntemi gibi yapmazsa, bu potansiyel olarak performansı, güvenliği, davranışı etkileyebilir (test etmedi mi? Geri kalanında authbind gerçekten esnek ve güvenli bir yöntemdir.
  • adres çevirimi, bağlantı durumu izleme vb. gerektirmediğinden iptables REDIRECT / DNAT yöntemini aşırı gerçekleştirir. Bu yalnızca yüksek yüklü sistemlerde fark edilir.

Duruma bağlı olarak, sysctl, CAP, authbind ve iptables-redirect arasında seçim yapardım. Ve bu çok fazla seçeneğimiz olması harika.


2
Harika cevap için teşekkürler! Görünüşe göre bu işlevsellik ilk olarak Nisan 2017'de Linux 4.11'de ortaya çıktı , bu yüzden bu soruyu ilk sorduğum 2009'da değildi. Ben de hızlı bir test yaptım ve "ipv4" sysctl adında olmasına rağmen IPV6 için de çalışıyor gibi görünüyor.
Jason Creighton

15

Diğer iki basit olasılık:

"Alçak bir limana bağlanan ve daemon'unuzu kontrol eden bir daemon" için eski (modaya uygun olmayan) bir çözüm var. Buna inetd (veya xinetd) denir. Eksileri:

  • sizin daemon'unuzun stdin / stdout üzerinde konuşması gerekiyor (eğer daemon'u kontrol etmiyorsanız - kaynağınız yoksa - bu belki de bir showtopper, ancak bazı hizmetlerin inetd-uyumluluk bayrağı olabilir)
  • her bağlantı için yeni bir arka plan programı süreci çatallanıyor
  • zincirde fazladan bir bağlantı var

Artıları:

  • herhangi bir eski UNIX'te mevcut
  • Sysadmin'iniz yapılandırmayı kurduktan sonra, geliştirme sürecinize devam etmekte fayda var (arka plan programınızı yeniden oluşturduğunuzda, setcap yeteneklerini kaybedebilir misiniz? Ve sonra yöneticinize geri dönmeniz gerekecek "lütfen efendim .. . ")
  • Daemon'un bu ağ işleri hakkında endişelenmesine gerek yok, sadece stdin / stdout hakkında konuşmalı
  • daemon'unuzu istendiği gibi root olmayan bir kullanıcı olarak yürütmek için yapılandırabilir

Başka bir alternatif: ayrıcalıklı bağlantı noktasından hedef arka plan programınızı çalıştırabileceğiniz rasgele yüksek numaralı bir bağlantı noktasına saldırıya uğramış bir proxy (netcat veya daha sağlam bir şey ). (Netcat açıkçası bir üretim çözümü değil, "sadece geliştirici kutum" değil mi?). Bu şekilde, sunucunuzun ağ özellikli bir sürümünü kullanmaya devam edebilirsiniz, proxy'yi başlatmak için yalnızca root / sudo'ya (önyükleme sırasında) ihtiyaç duyacaksınız, karmaşık / potansiyel olarak kırılgan yeteneklere dayanmayacaktır.


1
İyi öneri. Nedense inetd kullanmayı düşünmedim bile. Söz konusu hizmetin UDP tabanlı olması dışında TCP hizmetlerinden biraz daha karmaşıktır. Şu anda setcap ile çalışan bir çözümüm var, ama bunu biraz düşünmeliyim.
Jason Creighton

İkinci seçenek için, proxy'yi inetd kullanarak bile başlatabilirsiniz ... (Ancak IPTable'lar muhtemelen daha düşük ek yüktür ...)
Gert van den Berg

14

"Standart geçici çözümüm" kullanıcı alanı yeniden yönlendiricisi olarak socat kullanır:

socat tcp6-listen:80,fork tcp6:8080

Bunun ölçeklenmeyeceğine dikkat edin, çatallanma pahalıdır, ancak toplumun çalışma şekli budur.


13

Linux, "bu uygulama root olarak çalıştırılır" dan daha ayrıntılı izinleri destekleme yeteneklerini destekler. Bu özelliklerden biri, CAP_NET_BIND_SERVICEayrıcalıklı bir bağlantı noktasına bağlanmakla ilgilidir (<1024).

Ne yazık ki, bir uygulamayı hala root olarak çalıştırmak için nasıl kullanacağımı bilmiyorum CAP_NET_BIND_SERVICE(muhtemelen kullanıyor setcap, ancak bunun için mevcut bir çözüm olması gerekiyor).


Komut dosyaları veya tek tek JVM / mono programları için çalışmaz.
Stefan Steiger

13

Bu eski bir soru olduğunu biliyorum, ama şimdi son (> = 4.3) çekirdek ile nihayet bu iyi bir cevap var - ortam yetenekleri.

Hızlı yanıt, git'ten libcap'ın en son (henüz yayınlanmamış) sürümünün bir kopyasını almak ve derlemektir. Ortaya çıkan progs/capshikili dosyayı bir yere kopyalayın ( /usr/local/biniyi bir seçimdir). Ardından, root olarak programınızı

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

Sırayla, biz

  • Kullanıcı değiştirdiğimizde, mevcut yetenek setlerimizi korumak istediğimizi beyan etmek
  • Kullanıcı ve grubu 'hizmet-kullanıcı-adınız' olarak değiştirme
  • cap_net_bind_serviceDevralınan ve ortam setlerine yetenek ekleme
  • Forking bash -c 'your-command'( capshotomatik olarak bash başladığı için --)

Burada kaputun altında çok şey oluyor.

İlk olarak, root olarak çalışıyoruz, bu yüzden varsayılan olarak tam bir yetenek setine sahibiz. Bu, uys & gid ile setuidve setgidsyscalls arasında geçiş yapma yeteneğidir . Bununla birlikte, normalde bir program bunu yaptığında, yeteneklerini kaybeder - bu, kök salmanın eski yolu setuidhala çalışır. --keep=1Bayrak söyler capshsorunu prctl(PR_SET_KEEPCAPS)kullanıcıyı değiştirirken yetenekleri bırakarak devre dışı bırakır syscall. Tarafından kullanıcıların gerçek değişen capshile olur --userishal bayrak, setuidve setgid.

Çözmemiz gereken bir sonraki sorun, yeteneklerimizi execçocuklarımızdan sonra da devam edecek şekilde nasıl ayarlayacağımızdır . Yetenekler sistemi her zaman "bir yürütme (2) boyunca korunan bir dizi yetenek" olan bir "kalıtsal" yetenek grubuna sahiptir [ yetenekler (7) ]. Bu sorunumuzu çözüyor gibi görünse de (sadece cap_net_bind_servicemiras alma yeteneğini ayarlayın , değil mi?), Bu aslında sadece ayrıcalıklı işlemler için geçerlidir - ve işlemimiz artık ayrıcalıklı değildir, çünkü kullanıcıyı zaten değiştirdik (--user bayrakla).

Yeni ortam yeteneği kümesi bu soruna geçici bir çözüm sağlar - "ayrıcalıksız bir programın yürütülmesi (2) boyunca korunan bir dizi yetenek" dir. cap_net_bind_serviceOrtam setini koyarak , capshexec bizim sunucu programımız olduğunda, programımız bu özelliği devralacak ve dinleyicileri düşük bağlantı noktalarına bağlayabilecektir.

Daha fazla bilgi edinmek istiyorsanız, yetenekler manuel sayfası bunu ayrıntılı olarak açıklar. Koşu capshyoluyla straceda çok bilgilendirici!


--addambBayrak libcap 2.26 ile birlikte gelmektedir. Sistemimin 2,25'i vardı ve kaynaktan kaynak oluşturmak zorunda kaldım.
ngreen

12

TLDR: "Yanıt" için (gördüğüm gibi), bu cevaptaki >> TLDR << bölümüne gidin.

Tamam, bu sorunun cevabını (bu sefer gerçek) anladım ve bu cevabım da "en iyi" olduğunu düşündüğüm başka bir yanıtı (hem burada hem de Twitter'da) özür dilemenin bir yolu. ", ama denedikten sonra, bu konuda yanıldığımı keşfettim. Hata çocuklarımdan öğrenin: Gerçekten kendiniz denemeden bir şey tanıtmayın!

Burada tüm cevapları tekrar gözden geçirdim. Bazılarını denedim (ve başkalarını denememeyi seçtim çünkü çözümleri sevmedim). Ben çözüm kullanmak olduğunu düşündü systemdonun ile Capabilities=ve CapabilitiesBindingSet=ayarlara. Bir süre bununla güreştikten sonra bunun çözüm olmadığını keşfettim çünkü:

Yeteneklerin kök süreçleri kısıtlaması amaçlanmıştır!

OP akıllıca belirtildiği gibi, bundan kaçınmak her zaman en iyisidir (mümkünse tüm cinleriniz için!).

Birim dosyalarında User=ve Group=içinde Yeteneklerle ilgili seçenekleri kullanamazsınız systemd, çünkü özellikler (veya işlev ne olursa olsun) çağrıldığında yetenekler DAİMA sıfırlanır execev. Başka bir deyişle, systemdçatallar permalarını bırakıp bıraktığında , yetenekler sıfırlanır. Bunun hiçbir yolu yoktur ve çekirdekteki tüm bu bağlayıcı mantık yetenekler değil uid = 0 etrafında temeldir. Bu, Yeteneklerin bu soruya (en azından yakın zamanda) doğru cevap olacağı ihtimalinin düşük olduğu anlamına gelir. Bu arada, setcapdiğerlerinin de belirttiği gibi, bir çözüm değildir. Benim için çalışmadı, komut dosyalarıyla güzel çalışmıyor ve dosya her değiştiğinde bunlar yine de sıfırlanıyor.

Benim yetersiz savunmamda, (şimdi sildiğim yorumda), James'in iptables önerisinin (OP'nin de bahsettiği gibi) "2. en iyi çözüm" olduğunu söyledim . :-P

>> TLDR <<

Çözüm systemdanında ile birleştirmektiriptables komutlarla ( DNSChain'den alınmıştır ):

[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

Burada aşağıdakileri gerçekleştiriyoruz:

  • Daemon 5333'te dinliyor, ancak bağlantılar 53'te başarıyla kabul edildi. iptables
  • Komutları birim dosyasına dahil edebiliriz ve böylece insanların baş ağrısından tasarruf ederiz. systemdbizim için güvenlik duvarı kurallarını temizler, bu daemon çalışmadığında bunları kaldırdığınızdan emin olur.
  • Asla kök olarak koşmayız ve ayrıcalık yükselmesini imkansız hale getiririz (en azından systemdiddia eder), sözde daemon tehlikeye atılmış ve ayarlanmış olsa bile uid=0.

iptablesmaalesef oldukça çirkin ve kullanımı zor bir yardımcı program. Daemon dinlediğini eth0:0yerine eth0, örneğin, komutlardır biraz farklı .


2
Gerçekten eski bir Linux dağıtımına eth0:0sahip olmadıkça kimse artık eski tarz takma adlar kullanmamalıdır . Yıllardır kullanımdan kaldırıldı ve sonunda yok olacaklar.
Michael Hampton

1
Sanırım OpenVZ demek istiyorsun. (SolusVM bir kontrol paneli.) Ve evet, OpenVZ birçok şeyi yanlış yapıyor, ağ iletişimi bunlardan sadece biri.
Michael Hampton

1
Hayır, yani SolusVM. / Etc / network / arayüzlerinden:# Generated by SolusVM
Greg Slepak

1
Hala OpenVZ değil ... En azından ben kullanmıyorum ya da VPS'im.
Greg Slepak

7
Systemd'nin yetenekler özelliğine dikkat çektiğiniz için teşekkür ederiz. Ancak, systemd ikiliyi doğrudan başlattığında (komut dosyası değil) karmaşık iptables'lara gerek yoktur. Ayar AmbientCapabilities=CAP_NET_BIND_SERVICEile birlikte benim için mükemmel çalıştı User=.
Ruud

11

systemd , belirli özelliklere sahip bir arka plan programı başlatma seçeneğine sahip bir sysvinit değiştirme aracıdır. Seçenekler Yetenekler =, CapabilityBoundingSet = systemd.exec (5) kılavuzunda.


2
Bu cevabı daha önce tavsiye ettim, ama denedikten sonra şimdi yapmıyorum. Hala kullanan bir alternatif için cevabımı görün systemd.
Greg Slepak

10

Port yönlendirme bizim için en anlamlı olanıydı, ancak uygulamamızın yerel olarak yeniden yönlendirilmesi gereken bir URL'yi çözeceği bir sorunla karşılaştık; (bu da shindig anlamına gelir ).

Bu, yerel makinedeki URL'ye erişirken de yönlendirilmenizi sağlar.

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

Modern Linux destekler /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Systemd ile, önceden etkinleştirilen soketleri kabul etmek için hizmetinizi biraz değiştirmeniz yeterlidir.

Daha sonra sistemd soketi etkinleştirmeyi kullanabilirsiniz .

Hiçbir yetenek, iptables veya başka hile gerekmez.

Bu, basit python http sunucusu örneğindeki ilgili systemd dosyalarının içeriğidir

Dosya httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

Dosya httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Başlangıçta:

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

Ardından, yönlendirdiğiniz bağlantı noktasına bağlanabilirsiniz.


mu --to-portvar? man iptablessadece bahseder --to-ports(çoğul).
Abdull

ive bazı farklılıklar fark ettim ve etrafında atlamak
James

Bunu nasıl birleştireceğiniz için bu cevaba bakınız systemd.
Greg Slepak

3

Privbind yardımcı programını kullanın : ayrıcalıksız bir uygulamanın ayrılmış bağlantı noktalarına bağlanmasına izin verir.


3

Bir de 'djb yolu' var. İşleminizi tcpserver altındaki herhangi bir bağlantı noktasında çalışan kök olarak başlatmak için bu yöntemi kullanabilirsiniz, ardından işlemin başlamasından hemen sonra belirttiğiniz kullanıcıya işlemin denetimini verir.

#!/bin/sh

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

Daha fazla bilgi için bkz. Http://thedjbway.b0llix.net/daemontools/uidgid.html


1

OP sadece geliştirme / test olduğundan, şık çözümlerden daha azı yardımcı olabilir:

setcap, komut dosyalarına yetenekler kazandırmak için komut dosyasının yorumlayıcısında kullanılabilir. Genel yorumlayıcı ikili dosyasındaki setcap'ler kabul edilemezse, binary'in yerel bir kopyasını (herhangi bir kullanıcı olabilir) alın ve bu kopyaya setcap için root alın. Python2 (en azından) kod geliştirme ağacınızdaki yorumlayıcının yerel bir kopyasıyla düzgün çalışır. Kök kullanıcının, kullanıcıların hangi yeteneklere erişebileceğini denetleyebilmesi için suid gerekmez.

Tercüman için sistem genelinde güncellemeleri izlemeniz gerekiyorsa, komut dosyanızı çalıştırmak için aşağıdakine benzer bir kabuk komut dosyası kullanın:

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

İptables PREROUTING REDIRECT yöntemini denedim. Eski çekirdeklerde, bu tür bir kural IPv6 için desteklenmiyor gibi görünüyor . Ama görünüşe göre şimdi ip6tables v1.4.18 ve Linux çekirdeği v3.8'de destekleniyor.

PREROUTING REDIRECT'in makine içinde başlatılan bağlantılar için çalışmadığını da buldum. Yerel makineden bağlantılarda çalışmak için bir ÇIKIŞ kuralı da ekleyin - bkz. İptables bağlantı noktası yönlendirme yerel ana bilgisayar için çalışmıyor . Örneğin:

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

PREROUTING REDIRECT'in iletilen paketleri de etkilediğini gördüm . Yani, makine ayrıca arayüzler arasında paketleri de iletiyorsa (örneğin, bir Ethernet ağına bağlı bir Wi-Fi erişim noktası olarak işlev görüyorsa), iptables kuralı ayrıca bağlı istemcilerin İnternet hedeflerine olan bağlantılarını da yakalar ve makine. İstediğim bu değildi - sadece makinenin kendisine yönlendirilen bağlantıları yeniden yönlendirmek istedim. Ben ekleyerek, sadece kutuya adreslenmiş paketleri etkileyebilir yapabilirsiniz bulundu -m addrtype --dst-type LOCAL. Örneğin:

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

Diğer bir olasılık da TCP port yönlendirme kullanmaktır. Örneğin socat:

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

Bununla birlikte, bu yöntemin bir dezavantajı, 8080 numaralı bağlantı noktasını dinleyen uygulamanın, gelen bağlantıların kaynak adresini bilmemesidir (örneğin, günlük kaydı veya diğer tanımlama amaçları için).


0

2015 / Eylül'de cevap:

ip6tables artık IPV6 NAT'ı destekliyor: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Çekirdek 3.7'ye ihtiyacınız olacak

Kanıt:

[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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.