私以外のユーザーがいないとき、私の開発環境にこの制限があるのは非常に厄介です。
私は 標準的な回避策 を知っていますが、どれも私が望むことを正確には行いません。
Root以外のプロセスがLinux上の「特権」ポート(1024未満のポート)にバインドできるようにするための簡単なsysctl
変数がありますか。
編集:場合によっては、これを行うために ケーパビリティを使う できます。
そう、ケーパビリティシステムとCAP_NET_BIND_SERVICE
ケーパビリティを指摘してくれた人々のおかげです。最近のカーネルをお持ちの場合は、これを使用してサービスを非rootとして起動し、低いポートにバインドすることが可能です。簡単な答えはあなたがすることです:
setcap 'cap_net_bind_service=+ep' /path/to/program
そして、その後program
が実行されるときはいつでもCAP_NET_BIND_SERVICE
機能を持ちます。 setcap
は、Debianパッケージlibcap2-bin
にあります。
今注意するために:
program
やsetcap
のような昇格された特権を持つsuid
では、LD_LIBRARY_PATHを無効にします。それであなたのprogram
がそれ自身の.../lib/
を使うのであれば、ポート転送のような他のオプションを調べなければならないかもしれません。リソース:
setcap
に向けさせた文書。注: RHELはv6で最初にこれを追加しました 。
標準的な方法は、それらを "setuid"にしてrootとして起動してから、ポートにバインドした後でポートへの接続を受け入れる前にそのroot権限を捨てることです。 ApacheとINNのソースコードにその良い例があります。 Lighttpdもその良い例です。
もう1つの例はPostfixです。Postfixはパイプを通して通信する複数のデーモンを使い、そのうち1つか2つのみ(バイトの受け入れまたは送信を除く)はrootとして実行され、残りは低い特権で実行されます。
あなたはポートリダイレクトをすることができます。これは私がLinuxマシン上で実行されているSilverlightポリシーサーバーに対して行うことです
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
ローカルSSHトンネルを設定することができます。たとえば、ポート80を3000にバインドしたアプリにヒットさせたい場合は、次のようにします。
Sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N
これには、スクリプトサーバーを使用して作業するという利点があり、非常に単純です。
あるいはカーネルにパッチを当ててチェックを外してください。
(最後の手段の選択肢、お勧めできません)。
net/ipv4/af_inet.c
で、次の2行を削除します。
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto out;
そしてカーネルはもう特権ポートをチェックしません。
ファイルの機能は、パッケージの更新後に壊れる可能性があるため理想的ではありません。
理想的な解決策である私見は、継承可能な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をデーモンユーザーのものに変更します。 capsh
はGIDを変更せずに残しており、絶対に望まないため、これは必要です。$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"
他に2つの単純な可能性があります。
「低いポートにバインドしてあなたのデーモンに制御を渡すデーモン」に対する古い(昔ながらの)解決策があります。それはinetd(またはxinetd)と呼ばれます。短所は次のとおりです。
長所:
もう一つの選択肢:特権ポートからターゲットデーモンを走らせることができる任意の高い番号のポートへのハックアップされたプロキシ(netcatあるいは何かもっと堅牢)。 (Netcatは明らかにプロダクションソリューションではありませんが、「ちょうど私の開発者ボックス」ですね。)このようにして、ネットワーク対応バージョンのサーバーを使い続けることができ、(ブート時に)プロキシを起動するためにroot/Sudoだけが必要になり、複雑で脆弱な機能に頼ることはありません。
更新2017:
Authbindはユーザー/グループに信頼を与え、ポートごとのアクセスを制御し、IPv4とIPv6の両方をサポートします(最近IPv6サポートが追加されました)。
インストール:apt-get install authbind
関連するポートへのアクセスを設定します。すべてのユーザーおよびグループに対して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
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プログラムもシステムポートをバインドできることを意味します。
mono/.NETのDito。
私は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を使用し、この制限が二度と邪魔されないようにしたい場合はカーネルをコンパイルしてください。
私の「標準的な回避策」では、ユーザースペースリダイレクタとしてsocatを使用しています。
socat tcp6-listen:80,fork tcp6:8080
これは拡張できないことに注意してください。分岐は高価ですが、それがsocatの動作方法です。
私はこれが古い質問であることを知っていますが、最近の(> = 4.3)カーネルではついにこれに対する良い答えがあります - 周囲の能力。
手っ取り早い答えはlibcapの最新の(まだリリースされていない)バージョンのコピーを入手することです gitから そしてそれをコンパイルすること。結果のprogs/capsh
バイナリをどこかにコピーします(/usr/local/bin
が適切な選択です)。次に、rootとして、プログラムを起動します。
/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
--inh='cap_net_bind_service' --addamb='cap_net_bind_service' \
-- -c 'your-program'
順番に、私たちは
cap_net_bind_service
機能を追加するbash -c 'your-command'
の分岐(capsh
は--
の後の引数で自動的にbashを開始するため)ここでフードの下でたくさん起こっています。
まず、私たちはrootとして実行しているので、デフォルトで、私たちは機能のフルセットを取得します。これに含まれているのは、uid&gidをsetuid
およびsetgid
システムコールで切り替える機能です。しかし、通常、プログラムがこれを実行すると、その機能セットが失われます。これは、setuid
を使用してrootを削除する古い方法が依然として機能するためです。 --keep=1
フラグは、capsh
にprctl(PR_SET_KEEPCAPS)
syscallを発行するように指示します。これにより、ユーザーの変更時に機能の削除が無効になります。 capsh
によるユーザーの実際の変更は、setuid
およびsetgid
を実行する--user
フラグで発生します。
次に解決しなければならない問題は、子をexec
にした後も継続して機能を設定する方法です。ケーパビリティシステムは常に「継承された」ケーパビリティのセットを持っていました。それは「execve(2)を通して保存されたケーパビリティのセット」[ capabilities(7) ]。これで問題は解決したようですが(cap_net_bind_service
機能をinheritedに設定するだけですが)、これは実際には特権プロセスにのみ適用されます。すでにユーザーを変更したため(--user
フラグを使用)、プロセスは特権特権ではなくなります。
新しい周囲の能力セットはこの問題を回避します - それは「特権のないプログラムのexecve(2)を通して保存される能力のセットです」。 cap_net_bind_service
をアンビエントセットに入れることによって、capsh
execが私たちのサーバープログラムを実行するとき、私たちのプログラムはこの機能を継承し、リスナーを低いポートにバインドすることができます。
もっと知りたければ、ケーパビリティ manual page がこれを詳しく説明しています。 capsh
からstrace
までを実行することも非常に有益です。
Linuxは capabilities をサポートしており、単なる「このアプリケーションはrootとして実行される」よりもきめ細かい権限をサポートしています。これらの機能の1つはCAP_NET_BIND_SERVICE
で、これは特権ポート(<1024)へのバインドについてです。
残念ながら、CAP_NET_BIND_SERVICE
を付けたままで非rootとしてアプリケーションを実行するためにそれを悪用する方法はわかりません(おそらく setcap
を使用しますが、これには既存の解決策があるはずです)。
TLDR: "答え"については(私が見ているように)、この答えの>> TLDR <<の部分にジャンプしてください。
さて、私はそれを考え出しました(今回は実際のところ)、この質問への答え、そして私のこの答えも宣伝を謝罪する方法です 別の答え (ここでもTwitterでも) 「最高だ」と思ったのですが、試してみると、それが間違っていることがわかりました。私の過ちの子供たちから学ぶ:実際に試してみるまで、何かを宣伝しないでください。
繰り返しますが、ここですべての回答を確認しました。私はsomeそれらを試してみました(そして私は単純に解決策が好きではなかったので他の人を試しないようにしました)。私は解決策はCapabilities=
とCapabilitiesBindingSet=
設定でsystemd
を使うことだと思いました。しばらくこれと取り組んだ後、私はこれが解決策ではないことを発見しました理由:
ケーパビリティはルートプロセスを制限することを意図しています!
OPが賢明に述べたように、それを避けることが常に最善です(可能であれば、すべてのデーモンにとって!)。
systemd
ユニットファイル内でUser=
およびGroup=
と一緒にCapabilities関連オプションを使用することはできません。これは、execev
(または関数が何であれ)が呼び出されると、ケーパビリティがALWAYS resetとなるためです。つまり、systemd
がパーマを分岐してドロップすると、機能はリセットされます。これを回避する方法はありません。カーネル内のバインディングロジックはすべて、機能ではなくuid = 0を基本としています。これは、Capabilitiesがこの質問に対する正しい答えになるとは考えにくいということです(少なくとも近いうちに)。ちなみにsetcap
は、他の人が言っているように、解決策ではありません。それは私にはうまくいきませんでした、それはスクリプトでうまく動作しません、そしてそれらはファイルが変更されるたびにとにかくリセットされます。
私のわずかな防衛において、私は(私が今削除したコメントで)、James ' 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
ここで私たちは以下のことを達成します。
iptables
のおかげで53で接続は正常に受け入れられますsystemd
は私たちのためにファイアウォールのルールをクリーンアップし、デーモンが実行されていないときはそれらを確実に削除します。uid=0
を設定したとしても、私たちはrootとして走ることは決してなく、特権の昇格を不可能にします(少なくともsystemd
が主張する)。iptables
は、残念ながら、かなり醜くて使いにくいユーティリティです。たとえば、デーモンがeth0:0
ではなくeth0
をリッスンしている場合、コマンドは わずかに異なります です。
systemd はsysvinitに代わるもので、特定の機能を持つデーモンを起動するオプションがあります。オプションCapabilities =、CapabilityBoundingSet = in systemd.exec(5) マンページ。
ポートリダイレクトは私たちにとって最も理にかなっていましたが、私たちはアプリケーションがローカルに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
何らかの理由で、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
他の方法との比較この方法:
状況に応じて、sysctl、CAP、authbind、iptables-redirectのいずれかを選択します。そしてこれは素晴らしいことで、私たちにはたくさんの選択肢があります。
起動時:
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
それからあなたは転送先のポートにバインドすることができます。
Systemdを使用すると、アクティブ化されたソケットを受け入れるようにサービスを少し変更するだけで済みます。
あとで systemd socket activate を使うことができます。
機能、iptables、その他のトリックは必要ありません。
これはこの単純な例からの関連systemdファイルの内容です python http server
ファイル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
'djb way'もあります。この方法を使用すると、tcpserverの下の任意のポートでrootとして実行中のプロセスを起動できます。その後、プロセスの開始後すぐに、指定したユーザーにプロセスの制御が渡されます。
#!/bin/sh
UID=`id -u yourusername`
GID=`id -g yourusername`
exec tcpserver -u $UID -g $GID -RHl0 0 portnumber /path/to/your/process &
詳細については、参照してください: http://thedjbway.b0llix.net/daemontools/uidgid.html
privbind ユーティリティを使用してください。特権のないアプリケーションが予約済みポートにバインドできるようにします。
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 転送パケットにも影響します 。つまり、マシンがインタフェース間でパケットを転送している場合(たとえば、Ethernetネットワークに接続された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で待機しているアプリケーションが着信接続の送信元アドレスを知らないことです(ロギングやその他の識別の目的で)。
OPは単なる開発/テストなので、洗練されたソリューションではない場合があります。
setcapをスクリプトのインタプリタで使用して、スクリプトに機能を付与することができます。グローバルインタプリタバイナリのsetcapsが受け入れられない場合は、バイナリのローカルコピーを作成し(どのユーザでも可能です)、このコピーでsetcapのルートを取得します。 Python 2は(少なくとも)あなたのスクリプト開発ツリーのインタプリタのローカルコピーで適切に動作します。 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 $*
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
最新のLinuxは/sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0
をサポートしています。