マルチユーザー&root権限なしの環境で他ユーザーからTCPポートを保護する

sshサーバーなど、多くのユーザーが1つのオペレーティングシステムを共用するマルチユーザー環境では、TCP/UDPのようなポートを使用するアプリケーションを起動するとそれは他のユーザーからもアクセスできるようになります。これはセキュリティ的に望ましくありません。

もっとも、マルチユーザーのssh環境が使われている多くのケースではポートを使用するようなアプリケーションが必要ない(レンタルサーバーなど)あるいは共用するメンバーが信用できる(研究用途のサーバーなど)場合が多いですが、防御手段を考えておくに越したことはありません。

あまり取り上げられることのない話題かと思いますが、Hijacking other user’s TCP tunnelsなどではこの問題について触れられています。

この記事ではこの問題の解決法をちょっとだけ考えてみました。

ソフトウェア的な保護(パスワードなど)

当たり前ですが、まず有効なのが、各ソフトウェアが提供する認証のシステム(パスワードなど)を使用することです。ファイルサーバーなどであれば多くの場合はパスワード認証があります。

Unixドメインソケットを使ってssh転送

ただ、パスワードではなく、もっと根本的に、ポート自体が見えない状態にすることはできないか?とも思います。

そこで有効なのが、Unixドメインソケットを使う方法です。Unixドメインソケットとは、TCP/UDPポートのように動作する特殊なファイルのことです。ファイルなので、他のユーザーからはそもそもアクセスができません。ポートは他人が使っているとAddress in useになる可能性がありますがそれを避けられるメリットもあります。

例えばsshのポートフォワードでは、ドメインソケットを用いた転送(ローカルもリモートもどちらも)ができます(OpenSSH 6.7 will bring socket forwarding and more [LWN.net])。たとえば別のサーバーのポートを中継のsshサーバーに転送してそれをまたローカルに転送したいときなどは中継サーバーでドメインソケットを使用することができます。

Unixドメインソケットを強制的に使わせる?

Unixドメインソケットに対応していないソフト(というか、対応していないものの方が多いと思いますが)はどうすればいいんだ?と思うところですが、既存ソフトウェアに強制的にUnixドメインソケットを使わせることができるというソフトウェアがありました。

GitHub - cyphar/ttu: A small tool that silently converts TCP sockets to Unix sockets.

AppleのSystem Integrity Protectionとかに引っかからなければ一般ユーザー権限でも使えます。ただ、あくまで実験用にちょっと書いてみたという感じで、実用レベルのソフトウェアではなさそうです。listenもconnectもどっちも対応しています。TCPのみ対応です。LD_PRELOADを使っているので、システムコールを直接呼び出すプログラム(go製のものとか)では使えません(tsocksなどと同様)。

試しにPythonのhttp.serverとcurlという組み合わせでやってみたところ、アドレスが取得できないというようなエラーがPython側で出ました。やはり実用には制約が大きそうです。

感想

ちょっとだけになってしまってすみません。

やはりポートを他のユーザーから保護するのは結構難しそうです。そういうもんなんですかね…。ファイルシステムのようにちゃんと権限分離できれば、Webサービスとかでもう少し積極的にマルチユーザー環境を使っていけるような気もするんですが。