web-dev-qa-db-ja.com

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

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

私が尋ねる理由は、特権的なプロセスがソケットを開いて聞くことを許可することが愚かだと思うからです。ソケットを開いて待機するものはすべて危険度が高く、危険度の高いアプリケーションはrootとして実行しないでください。

Root権限で侵入したマルウェアを駆除するのではなく、特権のないプロセスがポート80を監視しているのかどうかを調べたいと思います。

90
jww

私はここに他の答えやコメントが何を参照しているのかわからない。これはかなり簡単に可能です。 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を使用してワンタイムアクセスを許可し、より細かいユーザー/グループ/ポート制御:

authbindマニュアルページ )ツールは、この目的のために存在します。

  1. 好きなパッケージマネージャを使って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
    

上記の両方には一長一短があります。オプション1はバイナリに信頼を与えますが、ポートごとのアクセスを制御することはできません。オプション2はユーザー/グループに信頼を与え、ポートごとのアクセスを制御しますが、残念ながらIPv4のみをサポートします。

141
Jason C

デール・ハグルンドがスポットです。それで、私は同じことを異なる方法で、いくつかの詳細と例を挙げて言うつもりです。 ☺

UnixおよびLinuxの世界で正しいことは次のとおりです。

  • スーパーユーザーとして実行され、リッスンソケットをバインドする、小さく、シンプルで、監査が容易なプログラムを用意する。
  • 最初のプログラムによって生成された、特権をドロップする別の小さく、シンプルで、監査が容易なプログラムを用意する。
  • 別のthirdプログラムでサービスの内容を保持するには、非スーパーユーザーアカウントで実行し、2番目のプログラムによってロードされたチェーンで、単純にソケットの開いているファイル記述子を継承します。

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

これを行うには多くの方法があります。

inetd

Dale Hagglundが言うように、古い「ネットワークスーパーサーバー」inetdがこれを行います。サービスプロセスが実行されるアカウントは、inetd.confの列の1つです。リスニング部分とドロップする特権部分を、小さくて監査しやすい2つの別個のプログラムに分離しませんが、メインサービスコードを別個のプログラムに分離します。それは、それが生成するサービスプロセスのexec()edソケットの開いているファイル記述子。

1つのプログラムを監査するだけでよいため、監査の難しさはそれほど問題ではありません。 inetdの主な問題は、それほど多くの監査ではなく、最新のツールと比較して、単純なきめ細かいランタイムサービス制御を提供しないことです。

UCSPI-TCPおよびdaemontools

Daniel J. Bernsteinの CSPI-TCP および daemontools パッケージは、これを組み合わせて行うように設計されています。あるいは、Bruce Guenterのほぼ同等の daemontools-encore toolsetを使用できます。

ソケットファイル記述子を開き、特権ローカルポートにバインドするプログラムは、UCSPI-TCPから tcpserver です。 listen()accept()の両方を実行します。

tcpserverは、ルート特権自体をドロップするサービスプログラムを生成します(提供されるプロトコルは、スーパーユーザーとして起動し、たとえばFTPやSSHデーモンの場合のように「ログオン」する必要があるため)または- setuidgid これは自己完結型の小さくて簡単に監査可能なプログラムであり、特権のみをドロップし、サービスプログラムにチェーンロードします(したがって、その一部は、たとえば、 qmail-smtpd )。

したがって、サービスrunスクリプトは、たとえば、次のようになります( dummyidentd の場合、これはヌルIDENTサービスを提供します)。

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

いや

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

繰り返しますが、単一目的のユーティリティが相互に生成され、チェーンがロードされます。設計の興味深い特徴の1つは、listen()の後、accept()の前でもスーパーユーザー特権を削除できることです。以下は、qmail-smtpdrunスクリプトです。

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

スーパーユーザーのイージスの下で実行されるプログラムは、サービスに依存しない小さなチェーン読み込みツールfdmoveclearenvenvdirsoftlimittcp-socket-listen、およびsetuidgidです。 shが開始されるまでに、ソケットは開かれてsmtpポートにバインドされ、プロセスにはスーパーユーザー特権がなくなります。

s6、s6-networking、execline

Laurent Bercotの s6 および s6-n​​etworking パッケージは、これを組み合わせて行うように設計されています。これらのコマンドは、daemontoolsおよびUCSPI-TCPのコマンドと構造的に非常に似ています。

runスクリプトは、 s6-tcpservertcpserverに、 s6-setuidgidsetuidgidに置き換えられることを除いて、ほぼ同じです。ただし、M。Bercotの execline ツールセットを同時に使用することもできます。

以下は、execline、s6、s6-networking、および publicfile のFTPサーバープログラムを使用する Wayne Marshallのオリジナル から少し変更した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と同じ行に沿って実行される別のツールセットです。ツールは今回はchpsttcpsvdですが、同じことを行い、信頼できないクライアントによってネットワーク経由で送信されたものの読み取り、処理、書き込みを行う高リスクコードはまだ別のプログラムにあります。

M。Papeの例 実行中 fnordrunスクリプト:

#!/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ディストリビューションにある新しいサービス監視および初期化システム inetdができることを行うことを目的としています 。ただし、小さな自己完結型プログラムのスイートは使用しません。残念ながら、systemd全体を監査する必要があります。

systemdを使用すると、構成ファイルを作成して、systemdがリッスンするソケットと、systemdが開始するサービスを定義します。サービスの「ユニット」ファイルには、実行するユーザーなど、サービスプロセスを大幅に制御できる設定があります。

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

26
JdeBP

私はかなり異なったアプローチをしています。 node.jsサーバーにポート80を使用したいと思いました。 Node.jsが非Sudoユーザー用にインストールされていたので、これを行うことができませんでした。シンボリックリンクを使おうとしましたが、うまくいきませんでした。

次に、あるポートから別のポートに接続を転送できることを知りました。そこで私はサーバをポート3000で起動し、ポート80からポート3000へのポート転送を設定しました。

このリンク はこれを行うために使用できる実際のコマンドを提供します。ここにコマンドがあります -

ローカルホスト/ループバック

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

外部の

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

私は2番目のコマンドを使用しました、そしてそれは私のために働きました。したがって、これはユーザープロセスが下位のポートに直接アクセスすることを許可せず、ポート転送を使用してアクセスできるようにするための中間的な理由と思います。

10
noob

あなたの本能は完全に正しいです。複雑で大規模なプログラムをrootとして実行するのは悪い考えです。

ただし、通常のユーザーが特権付きポートにバインドできるようにすることも賢明ではありません。そのようなポートは通常重要なシステムサービスを表すからです。

この明らかな矛盾を解決するための標準的なアプローチは、特権の分離です。基本的な考え方は、プログラムを2つ(またはそれ以上)の部分に分割することです。それぞれの部分は、アプリケーション全体の明確に定義された部分を処理し、単純な限られたインタフェースによって通信します。

あなたが与える例では、あなたはあなたのプログラムを2つの部分に分けたいです。 rootとして実行され、特権付きソケットを開いてバインドし、それを別の部分(通常のユーザーとして実行されている部分)に渡します。

この分離を達成するためのこれら二つの主要な方法。

  1. Rootとして起動する単一のプログラム。最初に行うことは、必要なソケットをできるだけ単純で制限された方法で作成することです。その後、特権を落とします。つまり、自分自身を通常のユーザーモードプロセスに変換し、その他すべての作業を行います。権限を正しく削除するのは難しいので、正しい方法を検討するために時間をかけてください。

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

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

4
Dale Hagglund