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


104

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

私が尋ねる理由は、特権プロセスがソケットを開いてリッスンできるようにするのは馬鹿げていると思うからです。ソケットを開いてリッスンするものはすべてリスクが高く、リスクの高いアプリケーションはルートとして実行しないでください。

ルート権限で潜伏したマルウェアを削除しようとするのではなく、ポート80で特権のないプロセスがリッスンしているものを把握しようとする方がはるかに好きです。


1
serverfault.com/questions/268099およびstackoverflow.com/questions/413807を参照してください。短い答えはノーです。
サミレイン14

10
長い答えは「はい」です。したがって、短い答えも「はい」である必要があります。
BT 14

4
簡単な答えイエスです。
ジェイソンC

回答:


163

ここの他の答えやコメントが何を指しているのか分かりません。これはかなり簡単に可能です。2つのオプションがあり、どちらもプロセスをルートに昇格させることなく、小さい番号のポートにアクセスできます。

オプション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ワンタイムアクセスを許可するために使用し、ユーザー/グループ/ポートをより細かく制御します。

authbindmanページ)ツールは、このために正確に存在しています。

  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のみをサポートしています。


本当にrwx許可が必要ですか?
マット

オプション1の操作を元に戻すには、-pintead of を使用してコマンドを再度実行し+eipますか?
eugene1832

5
setcapを使用して、特権を付与する実行可能ファイルを上書きすると(例:再構築を行う)、特権ポートステータスが失われ、再度特権を付与する必要があることに注意してください。
-rogerdpack

1
私がいじらなければならなかった何か。rubyを使用するruby実行可能ファイルを実行するsysvサービスを実行しようとしていました。あなたは与える必要がsetcap上の許可をバージョン固有ルビー実行例えば、/usr/bin/ruby1.9.1
クリスチャン・ロンドー

3
ファイルchmodを777に送信byportするのが最善のアイデアであることに疑問があります。からまでの範囲の許可を与えるのを見てきまし500744。私はあなたのために働く最も制限的なものに固執するでしょう。
ペレ

28

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

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

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

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

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

inetd

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

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

UCSPI-TCPおよびdaemontools

ダニエルJ.バーンスタインのUCSPI-TCPおよびdaemontoolsパッケージは、これを組み合わせて行うように設計されています。あるいは、Bruce Guenterのほぼ同等のdaemontools-encoreツールセットを使用できます。

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

tcpserver次に、ルート特権自体をドロップするサービスプログラムを生成します(サービス対象のプロトコルは、スーパーユーザーとして起動し、たとえばFTPまたはSSHデーモンの場合のように「ログオン」する必要setuidgidがあるため)。自己完結型の小さくて監査が容易なプログラムで、特権を単にドロップし、その後、適切なサービスプログラムにロードをチェーンします(したがって、その一部はスーパーユーザー特権で実行されませんqmail-smtpd

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

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

いや

私のnoshパッケージはこれを行うように設計されています。他のsetuidgidユーティリティと同様に、小さなユーティリティがあります。一つのわずかな違いは、それはで使用可能だということですsystemdスタイルの「LISTEN_FDS」のサービスだけでなく、UCSPI-TCPサービスと、その伝統的なtcpserverプログラムは、2つの別々のプログラムによって置き換えられますtcp-socket-listentcp-socket-accept

繰り返しますが、単一目的のユーティリティが相互に生成され、チェーンがロードされます。設計の1人の興味深い癖は1つが後にスーパーユーザ権限をドロップすることができるということですlisten()が、前であってもaccept()。実際runqmail-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 \
'

スーパーユーザの庇護の下で実行するプログラムは、小さなサービスに依存しないチェーンローディングツールですfdmoveclearenvenvdirsoftlimittcp-socket-listen、とsetuidgidsh開始された時点で、ソケットは開いてsmtpポートにバインドされており、プロセスにはスーパーユーザー特権がありません。

s6、s6-networking、execline

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

runs6-tcpserverfor tcpservers6-setuidgidforの置換を除いて、スクリプトはほとんど同じですsetuidgid。ただし、M。Bercotexeclineツールセットを同時に使用することもできます。

以下は、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.パプの例実行されているのfnordではrunスクリプトは:

#!/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残念ながら、全体を監査する必要があります。

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

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


2
お世辞に感謝します。これは具体的なアドバイスの素晴らしいコレクションです。+1。
デールハグルンド14

11

私にはかなり異なるアプローチがあります。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

外部

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

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


6
箱から出して考えて+1
リチャードワイズマン

4

あなたの本能は完全に正しいです。複雑で信頼性が高くなるため、大規模で複雑なプログラムをルートとして実行するのは悪い考えです。

しかし、通常のユーザーが特権ポートにバインドできるようにすることも悪い考えです。そのようなポートは通常、重要なシステムサービスを表しているためです。

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

この例では、プログラムを2つの部分に分割します。ルートとして実行し、特権ソケットを開いてバインドし、通常のユーザーとして実行する他の部分に何らかの方法でそれを渡します。

この分離を実現するこれらの2つの主な方法。

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

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

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


あなたの提案はベストプラクティスとはみなされません。特権ソケットをリッスンし、そのソケットを非特権プログラムに渡すことができるinetdを見ることができます。
デールハグルンド14

3

最も簡単な解決策: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でうまく機能します)

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


それは賢いです。1つの小さなnit:構成は80と443を開きますが、他のすべてのポートも開きます。他のポートの許可を緩和することは望ましくない場合があります。
jww
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.