非ルートプロセスがLinuxの「特権」ポートにバインドする方法はありますか?


389

私以外のユーザーがいない場合、私の開発ボックスにこの制限があることは非常に不愉快です。

私は標準的な回避策を知っていますが、どれも私が望むものを正確に実行しません:

  1. authbind(Debianテストのバージョン1.0、IPv4のみをサポート)
  2. iptables REDIRECTターゲットを使用して、低ポートを高ポートにリダイレクトします(「nat」テーブルは、iptablesのIPv6バージョンであるip6tablesにはまだ実装されていません)。
  3. sudo(ルートとして実行することは私が避けようとしていることです)
  4. SELinux(または類似)。(これは私の開発ボックスにすぎません。あまり複雑にしたくはありません。)

sysctl非ルートプロセスがLinuxの「特権」ポート(1024未満のポート)にバインドできるようにする簡単な変数はありますか、それとも私は運が悪いのですか?

編集:場合によっては、機能使用してこれを行うことができます


5
私の経験では、これを試みる1つの理由は、Apache(またはlighttpd)を使用するのではなく、Webサーバーを作成することです。
S.Lott、2009年


15
この場合、私はIPv6を使用していたため、「通常の」回避策(authbindおよびiptables REDIRECT)の一部が機能しませんでした。
Jason Creighton、2010年


1
それを行うにはいくつかの方法があります。非ルートプロセスがポート80および443にバインドすること許可しますか?スーパーユーザー。
jww

回答:


392

さて、能力システムとCAP_NET_BIND_SERVICE能力を指摘してくれた人々に感謝します。最近のカーネルを使用している場合は、これを使用して、非ルートとしてサービスを開始することができますが、低いポートをバインドします。簡単に言えば、次のことを行います。

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

そして、それprogram以降はいつでも実行CAP_NET_BIND_SERVICEできます。setcapはdebianパッケージに含まれていますlibcap2-bin

今警告のために:

  1. 少なくとも2.6.24カーネルが必要です
  2. ファイルがスクリプトの場合、これは機能しません。(つまり、#!行を使用してインタープリターを起動します)。この場合、私が理解している限り、インタープリター実行可能ファイル自体に機能を適用する必要があります。もちろん、そのインタープリターを使用するすべてのプログラムがその機能を持つため、これはセキュリティの悪夢です。この問題を回避するためのクリーンで簡単な方法を見つけることができませんでした。
  3. Linuxはprogramsetcapまたはのような昇格された権限を持つすべてのデバイスでLD_LIBRARY_PATHを無効にしますsuid。したがってprogram、独自のを使用する場合は、.../lib/ポート転送などの別のオプションを検討する必要がある場合があります。

リソース:

注:RHELはこれをv6で最初に追加しました


3
スクリプトの部分的な回避策:インタープリターのコピー(例:bash)を作成し、機能を提供しますが、コピーへのアクセスはそれを必要とするユーザーに制限します。もちろん、これらのユーザーは信頼されている必要がありますが、とにかくスクリプトを変更する可能性があります。
Erich Kitzmueller、2009年

3
前述のdebian(バイナリ)パッケージの他に、開発者のサイトはfriedhoff.org/posixfilecaps.html関連するpapers / presentations / etc ...
RandomNickName42

1
suse SLES 11.1では、これを機能させるために、カーネルparam file_caps = 1をgrub menu.lstに追加する必要がありました。
シェイ

2
はい、@ C.Rossを適用する必要があるため/usr/bin/java、システムで実行されているすべてのJavaアプリで機能を開くことができます。あまりにも悪い機能は、ユーザーごとに設定することもできません。
joeytwiddle 2013

5
setcap設定は再起動後も保持されますか?そうでない場合、システムの起動時に実行されるように、このルールを配置する標準的な場所はありますか?ある/etc/security/capability.conf任意のヘルプは、Debian / Ubuntuの上で?
joeytwiddle 2013

34

ポートのリダイレクトを行うことができます。これは、Linuxボックスで実行されているSilverlightポリシーサーバーに対して行うことです。

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

5
残念ながら、これはルーティングされた接続に対してのみ機能します。つまり、ローカルマシンからは機能しません。
zbyszek

@joeytwiddleサーバーを実行(開始)するスクリプトにあると思いますが、私は間違っている可能性があります。私も知りたいです:P
Camilo Martin 14

@CamiloMartinもう一度見ると、質問からリンクされたDebianドキュメントは、ファイアウォールスクリプトに配置するか、で作成することをお勧めします/etc/network/if-up.d/firewall
joeytwiddle 14

8
@zbyszek追加のiptablesのルールで、ローカルマシンの接続のためのこの缶の仕事、:stackoverflow.com/a/31795603/1356953
00500005

古いカーネルでは、これはIPv6ではサポートされていなかったようです。しかし、どうやらそれはip6tables v1.4.18とLinuxカーネルv3.8でサポートされています。
Craig McQueen

31

標準的な方法は、ルートとして起動するように「setuid」して、ポートにバインドした直後に、ポートへの接続の受け入れを開始する前に、ルート権限を破棄することです。ApacheとINNのソースコードでその良い例を見ることができます。Lighttpdも良い例だと聞いています。

もう1つの例はPostfixで、パイプを介して通信する複数のデーモンを使用し、そのうちの1つまたは2つ(バイトの受け入れまたは発行以外はほとんど機能しません)がrootとして実行され、残りはより低い特権で実行されます。


1
興味深いことに、これはCAP_SETUIDが設定されていないLinuxの最近のバージョン(おそらくUbuntu)では機能しません。したがって、setuidが必要な場合は、とにかくこの機能を設定する必要があります。
マット

これを行うには、ルート特権を削除するのが正しい方法です。init / upstart / systemdでサービスを開始する場合は、setuidを設定する必要はありません。
マイケルハンプトン

2
これは、プログラムがC#(Mono)、Java、Pythonなどのインタープリター型言語またはバイトコードインタープリターで記述されている場合は困難です。(どうやらPerlはbinfmt_misc'C'フラグを介してそれを行ったようです。他の人についてはわかりません。)
Craig McQueen

バイトを放出する以外はほとんどありません。
Ryan

バイナリのsetuidを設定すると、ルートプロセスとして実行されます。ただし、問題は、バイナリをルートプロセスとして実行せずにそれを達成する方法を尋ねます。この解決策は別の質問、つまり「非特権ユーザーがプロセスを特権ポートにバインドできるようにする方法」に対する答えですが、これは問題ではありません、IMHO?
マイケルビール

24

または、カーネルにパッチを適用してチェックを削除します。

(最後の手段のオプション、推奨されません)。

net/ipv4/af_inet.c、次の2行を削除します

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

カーネルは特権ポートをチェックしなくなります。


22
さまざまな理由から非常に悪い考えです。
Adam Lassek、

24
これは悪い考えです。しかし、実際には機能します。そして確かに私は笑わせました。
Oto Brglez

23
もちろんそれは悪い考えです。それが最後の手段だと言った理由です。オープンソースの要点は、あなたが望むように動かない場合、あなたがそれを変えることができるということです。
Joshua

18
なぜあなたが反対票を投じられたのかはわかりません。マルウェアの作成者は、どのポートをリッスンするかを気にしません。1024以上のポートを開いても大丈夫です。すべてのポートに特権が必要であるか、ポートに特権が必要ではないようです。また、1024未満のポートを開くことをルートに要求することは、ルートとして実行されているリスクの高いアプリケーションがあることを意味します。それは私には本当に馬鹿げた考えのようです。おそらくここに何か足りない...
jww

9
かつて、ポート<1024は、UNIXからUNIXへのプロトコルで使用され、反対側で実行されているコードがrootとして実行されていることを証明していました。これは、共通のセキュリティを備えた一連のUNIXサーバーに対しては適切に機能しました。
ジョシュア

22

ローカルのSSHトンネルを設定できます。たとえば、ポート80でアプリを3000にバインドする場合は、次のようにします。

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

これには、スクリプトサーバーを使用するという利点があり、非常にシンプルです。


1
私の個人的なお気に入り。オンとオフの切り替えが非常に簡単で、システム全体の変更は必要ありません。いい案!
Dan Passaro、2016

これは素晴らしいです。はるかに簡単な答え。
michaelsnowden 2017年

1
これは適度に高価であり、パフォーマンスが制限されている状況では良いアイデアではないと予想しますが、テストしないと確信が持てません。SSH暗号化のコストはゼロではありません。
lahwran

3
これはありませんsshのオーバーヘッドでnetcatをして行うことができる:sudo nc -l 80 | nc localhost 3000
Bugster

1
同じマシンの別のポートにデータを移動するためだけに暗号化+復号化していますか?また、これはUDPトラフィックでは機能しません。
ダニエルF

19

2017年更新:

authbindを使用する


CAP_NET_BIND_SERVICEまたはカスタムカーネルよりもはるかに優れています。

  • CAP_NET_BIND_SERVICEはバイナリに信頼を付与しますが、ポートごとのアクセスを制御しません。
  • authbindはユーザー/グループに信頼を与え、ポートごとのアクセスを制御し、IPv4とIPv6の両方をサポートします(IPv6のサポートは最近追加されました)。

    1. インストール: apt-get install 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、またはその他の引数を指定します。man ページを参照してください)。

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

      例えば

      authbind --deep java -jar SomeServer.jar
      

カーネルをハックするためのJoshuaのすばらしい(=何をすべきかを知らない限り推奨されない)推奨事項のフォローアップとして:

最初にここに投稿しました

シンプル。通常または古いカーネルでは、そうではありません。
他の人が指摘したように、iptablesはポートを転送できます。
他の人からも指摘されているように、CAP_NET_BIND_SERVICEもその役割を果たします。
もちろん、スクリプトからプログラムを起動すると、CAP_NET_BIND_SERVICEは失敗します。無意味なシェルインタープリターにキャップを設定しない限り、サービスをrootとして実行することもできます...
たとえば、Javaの場合は、それを適用する必要があります。 JAVA JVMへ

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

明らかに、それはつまり、あらゆるJavaプログラムがシステムポートをバインドできることを意味します。
dito for mono / .NET。

また、xinetdが最高のアイデアではないことも確信しています。
しかし、どちらの方法もハックなので、制限を解除して制限を解除してはどうでしょうか。
通常のカーネルを実行する必要があると誰も言っていないので、自分でカーネルを実行できます。

最新のカーネル(または現在お持ちのカーネル)のソースをダウンロードするだけです。その後、次の場所に移動します。

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

そこであなたはこの行を探します

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

そしてそれを

#define PROT_SOCK 0

安全でないssh状態にしたくない場合は、次のように変更します。#define PROT_SOCK 24

通常、必要な最低の設定を使用します。たとえば、httpの場合は79、ポート25でSMTPを使用する場合は24を使用します。

それだけです。
カーネルをコンパイルしてインストールします。
リブート。
完成-この愚かな制限はなくなりました。それはスクリプトでも機能します。

カーネルをコンパイルする方法は次のとおりです。

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

簡単に言えば、安全を確保したい場合はiptablesを使用し、この制限が二度と気にならないようにしたい場合はカーネルをコンパイルします。


反対票の理由は何ですか?ルート嫌い、またはカーネルコンパイル命令が機能しませんでしたか?
Stefan Steiger 2015年

4
カーネルを変更すると、将来の更新がさらに困難になります。カーネルを定期的に更新し続けるのが面倒なので、私はこれをしません。理論的には可能ですが、多くの状況で実行可能なオプションではありません。
kritzikratzi

グループの/ etc / authbind / byport / 23:たぶんそれを行うには、より安全な方法は、chownコマンドのログインよりもchmodの544の/ etc / authbind / byport / 23でのchmod 777の/ etc / authbind / byport / 80を変更することです
ジェローム・B

18

ファイル機能は、パッケージの更新後に壊れる可能性があるため、理想的ではありません。

理想的なソリューションであるIMHOは、継承可能な CAP_NET_BIND_SERVICEセットを。

これを行うには少し複雑な方法があります:

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

capshユーティリティは、Debian / Ubuntuディストリビューションのlibcap2-binパッケージにあります。これが起こっていることです:

  • sg実効グループIDをデーモンユーザーのIDに変更します。これは、capshGIDを変更せずに残しておく必要があるため、必要です。
  • ビット「UID変更時に機能を維持」を設定します。
  • UIDを $DAEMONUSER
  • --keep=1継承可能を除いて、すべてのキャップを削除します(現時点では、すべてのキャップはのためにまだ存在しています)。cap_net_bind_service
  • コマンドを実行します( '-'はセパレーターです)

結果は、指定されたユーザーとグループ、およびcap_net_bind_service特権を持つプロセスです。

例として、ejabberd起動スクリプトの行:

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

そして、それは実際には機能しません。キャップはユーザーIDスイッチでは保持されません。ルートとして実行したい場合は機能しますが、すべての機能が削除されています。
Cyber​​ax 2014

試してみると「バイナリファイルが実行できません」と表示されます。実際に、私は得ることができないcapsh行うには何も使用している場合、「バイナリファイルを実行することはできません」以外--==のオプションを。何が足りないのかしら。
Craig McQueen

@CraigMcQueen、その後のすべて--がに渡されるため/bin/bash、で試してみてください-c 'your command'。残念ながら、@ Cyber​​axと同じ問題が発生しているようですbind。しようとすると「アクセスが拒否されました」。
2015

これをファイル機能を設定せずに(またはrootとして実行して)機能させるには、このUnix.SE回答で説明されているようにアンビエント機能を使用する必要があります。また、使用することができ--gid代わりにsg(または--userそのセット--uid--gid、および--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

何らかの理由で、sysctl net.ipv4.ip_unprivileged_port_startを必要な値に下げることについて誰も言及していません。例:アプリを443ポートにバインドする必要があります。

sysctl net.ipv4.ip_unprivileged_port_start=443

潜在的なセキュリティ問題があると言う人もいます:非特権ユーザーが他の特権ポート(444-1024)にバインドする可能性があります。しかし、他のポートをブロックすることにより、iptablesでこの問題を簡単に解決できます。

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

他の方法との比較。この方法:

  • ある時点から(IMO)は、CAP_NET_BIND_SERVICE / setuidを設定するよりもさらに安全です。これは、アプリケーションが一部でも(実際には機能が)setuidしないためです。たとえば、機能が有効なアプリケーションのコアダンプをキャッチするには、sysctl fs.suid_dumpableを変更する必要があります(これにより、別の潜在的なセキュリティ問題が発生します)。また、CAP / suidが設定されている場合、/ proc / PIDディレクトリはrootが所有しているため、非rootユーザーは、実行中のプロセスの完全な情報/制御を持っていません。たとえば、ユーザーは(一般的なケースでは)/ proc / PID / fd /(netstat -aptn | grepを介してアプリケーションに属する接続を判別できません。 PID)。
  • にはセキュリティ上の欠点があります。アプリ(またはポート443〜1024を使用するすべてのアプリ)が何らかの理由でダウンしている間、別のアプリがポートを使用する可能性があります。ただし、この問題はCAP / suid(インタープリター(java / nodejsなど)で設定した場合)およびiptables-redirectにも適用される可能性があります。この問題を除外するには、systemd-socketメソッドを使用します。authbindメソッドを使用して、特別なユーザーバインディングのみを許可します。
  • 新しいバージョンのアプリケーションをデプロイするたびにCAP / suidを設定する必要はありません。
  • systemd-socketメソッドのようなアプリケーションのサポート/変更は必要ありません。
  • カーネルの再構築は必要ありません(実行中のバージョンがこのsysctl設定をサポートしている場合)
  • authbind / privbindメソッドのようにLD_PRELOADを実行しない場合、これはパフォーマンス、セキュリティ、動作に影響を与える可能性があります(テストされていませんか?)。残りの部分では、authbindは本当に柔軟で安全な方法です。
  • iptables REDIRECT / DNATメソッドを実行します。これは、アドレス変換、接続状態の追跡などを必要としないためです。これは、高負荷システムでのみ顕著です。

状況に応じて、sysctl、CAP、authbind、iptables-redirectから選択します。そして、これは素晴らしいことです。


2
すばらしい答えをありがとう!この機能は2017年4月Linux 4.11で初めて登場したようです。そのため、この質問を最初にしたのは2009年のことではありませんでした。私も簡単なテストを行いましたが、「ipv4」がsysctl名に含まれていても、IPV6でも機能するようです。
ジェイソンクレイトン2018

15

他の2つの単純な可能性:

「低いポートにバインドしてデーモンに制御を渡すデーモン」には、古い(時代遅れの)解決策があります。これは、inetd(またはxinetd)と呼ばれます。短所は次のとおりです。

  • デーモンはstdin / stdoutで通信する必要があります(デーモンを制御しない場合-ソースがない場合-これはおそらくshowtopperですが、一部のサービスにはinetd互換フラグがある場合があります)
  • 接続ごとに新しいデーモンプロセスがforkされます
  • チェーンの追加リンクの1つです

長所:

  • 古いUNIXで利用可能
  • システム管理者が設定をセットアップしたら、開発に取り掛かることができます(デーモンを再構築すると、setcap機能が失われる可能性がありますか?それから、管理者に戻る必要があります。 。」)
  • デーモンはネットワーキングに関することを心配する必要はなく、stdin / stdoutで話す必要があります
  • 要求に応じて、非rootユーザーとしてデーモンを実行するように構成できます

別の代替方法:特権ポートから、ターゲットデーモンを実行できる任意の番号の大きいポートへのハッキングされたプロキシ(netcatまたはさらに強力なもの)。(Netcatは明らかにプロダクションソリューションではありませんが、「ちょうど私の開発ボックス」ですよね?)この方法では、サーバーのネットワーク対応バージョンを引き続き使用でき、(起動時に)プロキシを開始するためにroot / sudoのみが必要であり、複雑な/潜在的に脆弱な機能に依存しません。


1
良い提案。何らかの理由で、inetdを使用することすら考えていませんでした。問題のサービスがUDPベースであることを除いて、TCPサービスよりも少し複雑です。私は現在setcapで機能する解決策を持っていますが、これについて少し考えなければなりません。
Jason Creighton、

2番目のオプションでは、おそらくinetdを使用してプロキシを開始することもできます(ただし、IPTablesはオーバーヘッドが低い可能性があります...)
Gert van den Berg

14

私の「標準的な回避策」では、ユーザー空間リダイレクタとしてsocatを使用しています。

socat tcp6-listen:80,fork tcp6:8080

これはスケーリングしないことに注意してください。フォークは高価ですが、それがsocatの動作方法です。


13

Linuxは、「このアプリケーションはrootとして実行される」だけではなく、よりきめ細かな権限をサポートする機能をサポートしています。これらの機能の1つはCAP_NET_BIND_SERVICE、特権ポート(<1024)にバインドすることです。

残念ながら、それを悪用してアプリケーションをroot以外のユーザーとして実行する方法はわかりませんCAP_NET_BIND_SERVICE(おそらく、setcapしますが、これには既存の解決策がある。


スクリプト、または個々のJVM /モノプログラムでは機能しません。
Stefan Steiger 2015年

13

私はこれが古い質問であることを知っていますが、最近のカーネル(> = 4.3)で、ようやくこれに対する良い答えがあります。アンビエント機能です。

簡単な答えは、gitから libcapの最新の(まだリリースされていない)バージョンのコピーを取得てコンパイルすることです。結果のprogs/capshバイナリをどこかにコピーします(これ/usr/local/binは適切な選択です)。次に、ルートとして、プログラムを起動します

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

順番に

  • ユーザーを切り替えるときに、現在の機能セットを維持することを宣言します
  • ユーザーとグループを「your-service-user-name」に切り替える
  • cap_net_bind_service継承セットとアンビエントセットに機能を追加する
  • フォークbash -c 'your-command'capsh後の引数でbashを自動的に開始するため--

ここでは、内部で多くのことが起こっています。

まず、rootとして実行しているため、デフォルトでは、すべての機能を利用できます。これには、setuidsetgidsyscalls でuidとgidを切り替える機能が含まれています。ただし、通常、プログラムがこれを行うと、一連の機能が失われます。これにより、ルートを削除する古い方法がsetuid引き続き機能します。--keep=1フラグが伝えcapsh発行しますprctl(PR_SET_KEEPCAPS)ユーザーを変更する際の能力の落下を無効にするシステムコールを、。による実際のユーザーの変更は、およびを実行capshする--userフラグで発生します。setuidsetgid

私たちが解決する必要がある次の問題は、私たちの後に続く方法で機能を設定する方法です exec、子供たちの。機能システムには常に「継承された」機能のセットがあり、これは「execve(2)全体で保持される機能のセット」[ capabilities(7) ]です。これは問題を解決するように聞こえますが(cap_net_bind_service機能を継承に設定するだけですよね?)、これは実際には特権プロセスにのみ適用されます-すでにユーザーを変更しているため(--userフラグを)。

新しいアンビエント機能セットは、この問題を回避します。これは、「特権のないプログラムのexecve(2)全体で保持される機能のセット」です。cap_net_bind_serviceアンビエントセットを入れることにより、capshexecがサーバープログラムである場合、プログラムはこの機能を継承し、リスナーを低いポートにバインドできます。

詳細については、機能のマニュアルページで詳しく説明されています。実行capsh通じことstraceも非常に有益です!


この--addambフラグはlibcap 2.26で導入されました。私のシステムは2.25で、ソースからビルドする必要がありました。
ngreen

12

TLDR:「答え」(私が見ているように)については、この答えの>> TLDR <<の部分にジャンプしてください。

OK、私はそれを(今回は実際に)理解しました。この質問への答えです。この私の答えは、別の答えを宣伝することを謝罪する方法でもあります、私が「最高の」と思った(こことTwitterの両方)あります「しかし、それを試した後、私がそれについて誤っていることに気づきました。私の間違いの子供たちから学んでください:実際に試してみるまで、何かを宣伝しないでください!

ここでも、すべての回答を確認しました。私はそれらのいくつかを試しました(そして、私は単にそのソリューションが気に入らなかったので、他のものを試さないことにしました)。私は、ソリューションを使用することだと思ったsystemdのでCapabilities=CapabilitiesBindingSet=設定。これにしばらく取り組んだ後、私はこれが解決策ではないことを発見しました:

機能はルートプロセスを制限するためのものです。

OPが賢明に述べたように、それを回避することは常に最善です(可能な場合はすべてのデーモンに!)

あなたはオプションで関連機能を使用することはできませんUser=Group=systemd機能がされているので、ユニットファイルを常にときにリセットexecev(または任意の関数である)と呼ばれています。つまり、systemdそのパーマをフォークおよびドロップすると、機能がリセットされます。これを回避する方法はなく、カーネル内のすべてのバインディングロジックは、機能ではなくuid = 0を中心にしています。これは、機能がこの質問に対する正しい答えになることはありそうもないことを意味します(少なくともいつでも)。ちなみに、setcap他の人が述べたように、は解決策ではありません。それは私にとってはうまくいきませんでした、それはスクリプトではうまく動きません、そしてそれらはファイルが変更されるたびにとにかくリセットされます。

私のわずかな弁護では、ジェームズのiptables提案(OPも言及している)が「2番目に最適なソリューション」であると(私は今削除したコメントで)述べました。:-P

>> TLDR <<

解決策は、次のようなsystemdオンザフライiptablesコマンドと組み合わせることです(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

ここでは、次のことを行います。

  • デーモンは5333でリッスンしますが、接続は53で正常に受け入れられます。 iptables
  • ユニットファイル自体にコマンドを含めることができるため、人々の頭痛を軽減できます。systemdファイアウォールルールをクリーンアップして、デーモンが実行されていないときに必ず削除します。
  • rootとして実行することは決してなくsystemd、おそらくデーモンが危険にさらされて設定されたとしても、特権エスカレーションを不可能にします(少なくとも主張します)uid=0

iptables残念ながら、それでも非常に醜く、使いにくいユーティリティです。たとえば、デーモンがではeth0:0なくリッスンしている場合、eth0コマンドは少し異なります。


2
eth0:0彼らが本当に古代の Linuxディストリビューションを持っているのでなければ、誰もがもう古いスタイルのエイリアスを使うべきではありません。それらは何年もの間廃止されており、最終的にはなくなるでしょう。
マイケルハンプトン

1
私はあなたがOpenVZを意味すると思います。(SolusVMはコントロールパネルです。)そして、はい、OpenVZは多くのことを間違っています。ネットワーキングはその1つにすぎません。
マイケルハンプトン

1
いや、私はSolusVMを意味します。/ etc / network / interfacesから:# Generated by SolusVM
Greg Slepak、2014

1
まだOpenVZではありません...少なくとも私はそれを使用しておらず、VPSも使用していません。
Greg Slepak 14

7
systemdの機能機能を指摘していただきありがとうございます。ただし、systemdが(スクリプトではなく)バイナリを直接起動する場合は、複雑なiptablesは必要ありません。AmbientCapabilities=CAP_NET_BIND_SERVICEとの組み合わせで、設定は完全にうまくいきましたUser=
Ruud

11

systemdは、特定の機能を持つデーモンを起動するオプションがあるsysvinitの代替品です。systemd.exec(5)マンページのオプションCapabilities =、CapabilityBoundingSet = 。


2
以前はこの回答をお勧めしましたが、試した後はお勧めしません。まだ使用している別の方法については、私の回答を参照しくださいsystemd
Greg Slepak 14

10

私たちにとってはポートリダイレクトが最も理にかなっていますが、アプリケーションがローカルにURLを解決し、これも再ルーティングする必要があるという問題に遭遇しました。(それはあなたがshindigを意味する)。

これにより、ローカルマシンのURLにアクセスするときにリダイレクトすることもできます。

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


8

systemdでは、事前にアクティブ化されたソケットを受け入れるようにサービスをわずかに変更する必要があります。

後でsystemdソケットアクティベートを使用できます

機能、iptables、その他のトリックは必要ありません。

これは、シンプルなpython httpサーバーのこの例の関連するsystemdファイルの内容です

ファイル httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

ファイル httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

起動時:

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

次に、転送先のポートにバインドできます。


ない--to-port存在?man iptables言及のみ--to-ports(複数)。
Abdull

私はいくつかの違いに気づき、私は飛び回っています
James

これをと組み合わせる方法については、この回答を参照してくださいsystemd
Greg Slepak 14


3

'djb way'もあります。このメソッドを使用して、tcpserverの下の任意のポートで実行されているrootとしてプロセスを開始できます。その後、プロセスの開始直後に、指定したユーザーにプロセスの制御を渡します。

#!/bin/sh

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

詳細については、http//thedjbway.b0llix.net/daemontools/uidgid.htmlを参照してください。


1

OPは単なる開発/テストであるため、洗練されたソリューションよりも役立つ場合があります。

スクリプトのインタープリターでsetcapを使用して、スクリプトに機能を付与できます。グローバルインタープリターバイナリのsetcapsが受け入れられない場合は、バイナリのローカルコピーを作成し(すべてのユーザーが可能)、このコピーのrootにsetcapを取得します。Python2は(少なくとも)スクリプト開発ツリー内のインタープリターのローカルコピーで適切に動作します。rootユーザーがユーザーがアクセスできる機能を制御できるように、suidは必要ありません。

インタープリターに対するシステム全体の更新を追跡する必要がある場合は、次のようなシェルスクリプトを使用してスクリプトを実行します。

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

iptables PREROUTING REDIRECTメソッドを試しました。古いカーネルでは、このタイプのルールはIPv6ではサポートされていなかったようですです。しかし、どうやらip6tables v1.4.18とLinuxカーネルv3.8でサポートされているようです。

また、マシン内で開始された接続に対してPREROUTING REDIRECTが機能しないこともわかりました。ローカルマシンからの接続を処理するには、OUTPUTルールも追加します— iptablesポートリダイレクトがlocalhostで機能しないを参照してください。例:

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

PREROUTING REDIRECT も転送されたパケットに影響を与えることがわかりました。つまり、マシンがインターフェイス間でパケットも転送している場合(イーサネットネットワークに接続されたWi-Fiアクセスポイントとして機能している場合など)、iptablesルールは、インターネット接続先への接続されたクライアントの接続もキャッチし、それらをリダイレクトします。この機械。それは私が望んでいたことではありません。マシン自体に向けられた接続のみをリダイレクトしたかったのです。を追加することで、ボックス宛のパケットにのみ影響を与えることができることがわかりました-m addrtype --dst-type LOCAL。例:

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

もう1つの可能性は、TCPポート転送を使用することです。例socat

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

ただし、この方法の1つの欠点は、ポート8080でリッスンしているアプリケーションが着信接続の送信元アドレスを認識しないことです(たとえば、ログまたはその他の識別目的のため)。


0

2015年9月の回答:

ip6tablesがIPV6 NATをサポートするようになりました: http //www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

カーネル3.7以降が必要です

証明:

[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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.