2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117
117

非ルートプロセスがポート 80 と 443 にバインドすることを許可するか?

ユーザーランドプログラムが80番ポートと443番ポートにバインドできるようにカーネルパラメータを調整することは可能でしょうか?

私が尋ねる理由は、特権プロセスがソケットを開いてリッスンすることを許可するのは愚かだと思うからです。ソケットを開いてリッスンするものはリスクが高く、リスクの高いアプリケーションは root で実行すべきではありません。

root 権限で潜り込んだマルウェアを除去しようとするよりも、どのような非特権プロセスが 80 番ポートをリッスンしているのかを解明しようとする方がずっといいと思います。

回答 (5)

176
176
176
2015-03-21 21:12:41 +0000

他の回答やここのコメントが何を参考にしているのかよくわかりません。これはわりと簡単に可能です。2つのオプションがあり、どちらもプロセスを root に昇格させることなく低番号ポートへのアクセスを許可します。

オプション1: CAP_NET_BIND_SERVICE を使用してプロセスに低番号ポートアクセスを許可する:

これを使用すると、setcap コマンドを使用して低番号ポートにバインドするために特定のバイナリへの永続的なアクセスを許可することができます。

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

e/i/pの部分の詳細については、 cap_from_text を参照してください。

これを行うと、/path/to/binaryは低番号のポートにバインドできるようになります。setcap はシンボリックリンクではなく、バイナリ自体に使用しなければならないことに注意してください。

オプション2: authbindを使用してワンタイムアクセスを許可し、より細かいユーザ/グループ/ポート制御を行う:

(http://en.wikipedia.org/wiki/Authbind) authbind [ man page ]0x3&) ツールはこのために正確に存在します。

1.お気に入りのパッケージマネージャを使ってauthbindをインストールする。

  1. 関連するポートへのアクセスを許可するように設定します。authbind (オプションで--deepやその他の引数を指定することもできます。マニュアルページを参照)


上記のどちらにもメリットとデメリットがあります。オプション1はbinaryに信頼を付与しますが、ポート毎のアクセスは制御できません。オプション2はuser/groupに信頼を付与し、ポート毎のアクセスを制御しますが、古いバージョンではIPv4しかサポートしていませんでした(これを最初に書いた時から、IPv6をサポートする新しいバージョンがリリースされています)。

29
29
29
2014-02-02 16:21:39 +0000

Dale Hagglundはその通りだ。

  • スーパーユーザとして動作し、リスニングソケットをバインドする小さなプログラムを持つこと。

  • スーパーユーザとして動作し、リスニングソケットをバインドする、小さくてシンプルで簡単に聴取可能なプログラムを用意すること。

あなたはリスクが高い場所について間違った考えを持っています。リスクが高いのは、ソケットを開いてポートにバインドし、listen()を呼び出すという単純な行為ではなく、_ネットワークから読み込んで、読み込んだものに基づいて行動することにあります。リスクが高いのは、実際の通信を行うサービスの部分です。開く部分、bind()listen()accepts()、さらには(ある程度)accept()を呼び出す部分はリスクが高くなく、スーパーユーザの庇護の下で実行することができます。彼らはネットワーク上で信頼できない他人の管理下にあるデータを(inetdの場合はソースIPアドレスを除いて)使用して行動することはありません。

これには様々な方法がある。

inetd

Dale Hagglundが言うように、古い「ネットワークスーパーサーバ」inetd.confがこれを行っている。サービスプロセスが実行されているアカウントは、exec()のカラムの1つです。これは、リスニング部分と特権のドロップ部分を2つの別のプログラムに分離しているわけではなく、小さくて簡単に聴取できるが、メインのサービスコードを別のプログラムに分離し、ソケット用のオープンファイルの記述子を使って生成するサービスプロセスの中のinetdにしている。

監査の難しさは、1つのプログラムを監査するだけなので、それほど問題ではありません。tcpserverの大きな問題は、監査というよりも、最近のツールと比較して、シンプルで細かいランタイムサービス制御を提供していないことです。

UCSPI-TCP と daemontools

Daniel J. Bernstein 氏の UCSPI-TCP daemontools のパッケージは、これを併用するように設計されています。代わりに、Bruce Guenter氏のほぼ同等のツールセットである daemontools-encore を使用することもできます。

ソケットファイルディスクリプタを開き、特権ローカルポートにバインドするプログラムは、UCSPI-TCP の listen() である。accept()tcpserverの両方を行います。

setuidgid はその後、ルート権限を削除するサービスプログラムを生成するか(サービスを提供するプロトコルには、例えば、スーパーユーザとして起動してから「ログオン」することが含まれているため)、ルート権限を削除するサービスプログラムを生成します。これは、権限を削除してから適切なサービスプログラムにロードをチェーンするだけの、自己完結型の小さくて簡単に監査可能なプログラムです( qmail-smtpd のように、スーパーユーザ権限で実行されることはありません)。

サービスrunスクリプトは以下のようになります(これはnull IDENTサービスを提供するための dummyidentd 用のものです):

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

nosh

私のnoshパッケージ はこれを行うために設計されています。他のものと同じように、小さな setuidgid ユーティリティを持っています。1つのわずかな違いは、UCSPI-TCPサービスと同様にsystemdスタイルの “LISTEN_FDS "サービスでも使用できるということで、従来のtcpserverプログラムは2つの別々のプログラムに置き換えられます。tcp-socket-listentcp-socket-accept です。

ここでも、単一目的のユーティリティがスポーンされ、互いにチェーンロードされます。この設計の興味深い奇抜さの一つは、listen() の後ではなく、accept() よりも前にスーパーユーザ権限を削除できるということです。以下に run 用の qmail-smtpd スクリプトを示します。

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

スーパーユーザの庇護の下で実行されるプログラムは、小さなサービスに依存しないチェインローディングツール fdmove, clearenv, envdir, softlimit, tcp-socket-listen, setuidgid, sh, smtp, および daemontools である。runが起動された時点で、ソケットはオープンでs6-tcpserverポートにバインドされており、プロセスはスーパーユーザ権限を持たなくなります。

s6, s6-networking, and execline

Laurent Bercot の s6 s6-networking パッケージは、このようなことを同時に行うように設計されています。これらのコマンドは、構造的にはtcpserverとUCSPI-TCPと非常によく似ています。

s6-setuidgid のスクリプトは、 setuidgid chpst に、 tcpsvd fnord に置き換えた以外はほとんど同じである。しかし、M. Bercotの execline ツールセットを同時に利用することもできます。

以下は Wayne Marshall’s original を軽く修正したFTPサービスの例で、execline、s6、s6-networking、および publicfile のFTPサーバプログラムを使用しています。

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

Gerrit Papeの[ipsvd は、ucspi-tcpとs6-networkingと同じラインで動作する別のツールセットです。今回のツールはrunsystemdですが、やっていることは同じで、ネットワーク経由で送られてきたものの読み取り、処理、書き込みを 信頼されていないクライアントはまだ別のプログラムの中にあります。

ここに、systemd スクリプトで inetd を実行した M. Pape の例 があります。

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd は、いくつかのLinuxディストリビューションで見られる新しいサービス監視およびinitシステムで、 systemdができることを目的としているです。しかし、小さな自己完結型のプログラム群を使用しているわけではありません。残念ながら、systemd全体を監査しなければならない。

systemdでは、listen()がリッスンするソケットと、accept()が起動するサービスを定義するための設定ファイルを作成する。サービスの「ユニット」ファイルには設定があり、どのユーザで実行するかなど、サービスプロセスを大幅に制御できるようになっている。

そのユーザを非スーパーユーザに設定すると、0x6&はソケットを開き、ポートにバインドし、プロセス#1の0x6&(および必要に応じて0x6&)をスーパーユーザとして呼び出し、生成されたサービスプロセスはスーパーユーザ権限なしで実行される。

17
17
17
2018-06-27 07:00:56 +0000

私はどちらかというと違うアプローチをしています。node.jsサーバーに80番ポートを使いたかったのです。Node.jsは非sudoユーザー用にインストールされていたので、できませんでした。シンボリックリンクを使ってみたのですが、私の場合はうまくいきませんでした。

それから、あるポートから別のポートに接続を転送できることを知りました。そこで、3000番ポートでサーバを起動して、80番ポートから3000番ポートへのポートフォワードを設定してみました。 このリンク に、これを行うために使用できる実際のコマンドが記載されています。

localhost/loopback

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

external

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

私は2番目のコマンドを使ってみましたが、うまくいきました。なので、ユーザープロセスに直接下位ポートへのアクセスを許可せず、ポートフォワードを利用してアクセスを与えるという中間地点になるのではないかと思います。

4
4
4
2014-02-02 06:49:22 +0000

あなたの直感は完全に正しいです。大規模で複雑なプログラムを root 権限で実行させるのは良くない考えです。

しかし、一般ユーザに特権ポートへのバインドを許可するのも良くない考えです。

この明らかな矛盾を解決するための標準的なアプローチは privilege separation です。基本的な考え方は、プログラムを 2 つの(あるいはそれ以上の)部分に分割し、それぞれがアプリケーション全体の中で明確に定義された部分を実行し、シンプルで限定されたインターフェイスで通信を行うというものです。

あなたの例では、プログラムを2つの部分に分割したいとします。1つはrootとして実行され、特権ソケットを開いてバインドし、もう1つは通常のユーザとして実行されます。

この分離を実現するには、主に以下の2つの方法があります。

1.root で起動する単一のプログラム。最初に行うことは、必要なソケットを、できるだけシンプルで限定的な方法で作成することです。その後、権限を削除し、通常のユーザモードプロセスに変換し、他のすべての作業を行います。正しく権限を削除するのは難しいので、時間をかけて正しい方法を勉強してください。

  1. 親プロセスが作成したソケットペアを介して通信するプログラムのペア。非特権ドライバプログラムは初期引数を受け取り、おそらく基本的な引数検証を行います。非特権のドライバプログラムは、初期引数を受け取り、基本的な引数の検証を行い、socketpair()を介して接続されたソケットのペアを作成し、実際の作業を行う他の2つのプログラムをフォークして実行し、ソケットペアを介して通信します。これらのうちの1つは特権的な処理で、サーバソケットの作成やその他の特権的な処理を行い、もう1つはより複雑で信頼性の低いアプリケーションの実行を行います。

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

3
3
3
2019-09-13 07:38:46 +0000

最も簡単な解決策 : linux上のすべての特権ポートを削除

ubuntu/debian上で動作 :

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

(rootアカウントでないVirtualBoxでも動作)

さて、すべてのユーザーがすべてのポートをバインドできるので、セキュリティには十分注意してください!

すべてのユーザーがすべてのポートをバインドすることができます。