[インデックス 10077] ファイルの概要
このコミットは、Go言語のネットワークパッケージにおいて、Windows環境でのソケットオプション SO_REUSEADDR の設定を削除するものです。これにより、Windowsにおける SO_REUSEADDR の挙動に起因する潜在的な問題が回避されます。
コミット
- コミットハッシュ:
c1d0f0e76c4294abc1eb8464c06fb329717a7cdb - 作者: Alex Brainman alex.brainman@gmail.com
- コミット日時: Mon Oct 24 09:11:01 2011 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c1d0f0e76c4294abc1eb8464c06fb329717a7cdb
元コミット内容
net: do not set SO_REUSEADDR for windows
Fixes #2307.
R=golang-dev, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5306049
変更の背景
このコミットの背景には、SO_REUSEADDR ソケットオプションがWindowsとUnix系OS(Linuxなど)で異なる挙動を示すという重要な技術的差異があります。
Go言語のネットワークパッケージは、クロスプラットフォームで動作するように設計されています。しかし、SO_REUSEADDR の挙動の違いがWindows環境で問題を引き起こしていました。具体的には、Unix系OSでは SO_REUSEADDR は主に TIME-WAIT 状態にあるソケットのアドレスとポートの再利用を許可するために使用されますが、Windowsではより広範な意味を持ちます。Windowsでは、SO_REUSEADDR を設定すると、既に別のプロセスによって使用されているアドレスとポートに新しいソケットがバインドできてしまう可能性があります。これは「ポートの乗っ取り(port stealing)」として知られ、セキュリティ上の脆弱性や予期せぬ動作の原因となることがあります。
この問題は、GoのIssue #2307として報告されており、このコミットはその問題を解決するために sock_windows.go から SO_REUSEADDR の設定を削除しています。
前提知識の解説
ソケットオプション SO_REUSEADDR
SO_REUSEADDR は、ソケットがバインドするアドレスとポートの再利用を制御するためのソケットオプションです。
-
Unix系OS (Linux, macOSなど) での挙動:
TIME-WAIT状態のポートの再利用: 主に、サーバーアプリケーションがシャットダウンされた後、そのソケットがTIME-WAIT状態にある間に、同じアドレスとポートにすぐに再バインドできるようにするために使用されます。TIME-WAIT状態は、TCP接続が終了した後、ネットワーク上の遅延パケットが到着するのを待つために一定期間(通常は数分)維持される状態です。SO_REUSEADDRがないと、この期間中は同じポートにバインドできません。- 異なるプロセスによる同時バインドの防止:
SO_REUSEADDRが設定されていても、通常、異なる2つのプロセスが全く同じIPアドレスとポートの組み合わせに同時にバインドしてリッスンすることはできません。 - ワイルドカードアドレスの扱い:
0.0.0.0のようなワイルドカードアドレスの扱いにも影響します。SO_REUSEADDRが設定されている場合、0.0.0.0:portにバインドした後で特定のIPアドレス (192.168.0.1:portなど) にバインドすることも可能になります。 SO_REUSEPORT: Linuxカーネル3.9以降では、SO_REUSEPORTというオプションが導入されました。これは、複数のプロセスが同じアドレスとポートにバインドすることを明示的に許可し、ロードバランシングなどに利用されます。ただし、ポートハイジャックを防ぐためのセキュリティメカニズムも備わっています。
-
Windows での挙動:
- アドレスの「乗っ取り」: Windowsにおける
SO_REUSEADDRは、Unix系OSよりもはるかに広範な意味を持ちます。これを設定すると、既に別のプロセスによって使用されているアドレスとポートに、新しいソケットがバインドできてしまいます。これは、意図しないアプリケーションがサービスを乗っ取ったり、セキュリティ上の問題を引き起こしたりする可能性があります。 SO_EXCLUSIVEADDRUSE: Windowsでは、Unix系OSのSO_REUSEADDRに近い、より厳密なバインド動作を実現するためにSO_EXCLUSIVEADDRUSEオプションが提供されています。このオプションを設定してソケットが正常にバインドされると、他のどのソケットも同じアドレスとポートにバインドできなくなります。SO_REUSEPORTの欠如: WindowsにはSO_REUSEPORTに相当するオプションはありません。
- アドレスの「乗っ取り」: Windowsにおける
TIME-WAIT 状態
TCP接続が正常に終了する際に、最後に接続を閉じた側(通常はクライアントだが、サーバーの場合もある)のソケットが移行する状態です。この状態は、ネットワーク上で遅延しているパケット(例えば、FINやACKの再送)が到着するのを待ち、それらを適切に処理するために存在します。TIME-WAIT 状態のソケットは、一定期間(通常は2MSL: Maximum Segment Lifetime、最大セグメント寿命の2倍)ポートを占有します。この期間中に同じポートに新しいソケットをバインドしようとすると、通常は「Address already in use」エラーが発生します。
技術的詳細
Go言語のネットワークパッケージは、サーバーアプリケーションを構築する際によく利用されます。サーバーは通常、特定のポートにバインドしてクライアントからの接続を待ち受けます。この際、サーバーが再起動されることを考慮すると、TIME-WAIT 状態のポートを再利用できる SO_REUSEADDR はUnix系OSでは非常に便利です。
しかし、Windows環境で SO_REUSEADDR を無条件に設定することは、以下のような問題を引き起こす可能性があります。
- ポートの乗っ取り: 悪意のある、または単に設定ミスのある別のアプリケーションが、既にGoアプリケーションが使用しているポートにバインドできてしまう可能性があります。これにより、正当なサービスが妨害されたり、データが傍受されたりするリスクが生じます。
- 予期せぬ動作: 開発者が意図しない形で、複数のアプリケーションが同じポートを共有してしまうことで、デバッグが困難な問題や、サービスの不安定化を招く可能性があります。
- セキュリティリスク: 特に、特権の低いプロセスが特権の高いプロセスが使用しているポートを乗っ取ることが可能になる場合、深刻なセキュリティ脆弱性につながります。
このコミットは、GoのネットワークパッケージがWindows上でソケットを作成する際に、デフォルトで SO_REUSEADDR を設定しないようにすることで、これらの問題を回避しています。これにより、Windowsのデフォルトのソケットバインド動作が尊重され、より安全で予測可能な挙動が保証されます。もしWindows上で TIME-WAIT 状態のポートを再利用したい場合は、アプリケーションレベルで SO_EXCLUSIVEADDRUSE を使用するなど、Windows固有のAPIを明示的に利用する必要があります。
コアとなるコードの変更箇所
変更は src/pkg/net/sock_windows.go ファイルの1箇所のみです。
--- a/src/pkg/net/sock_windows.go
+++ b/src/pkg/net/sock_windows.go
@@ -11,9 +11,6 @@ import (
)
func setKernelSpecificSockopt(s syscall.Handle, f int) {
- // Allow reuse of recently-used addresses and ports.
- syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
-
// Allow broadcast.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
コアとなるコードの解説
src/pkg/net/sock_windows.go ファイル内の setKernelSpecificSockopt 関数は、Windows環境でソケットにカーネル固有のソケットオプションを設定するために使用されます。
このコミットでは、以下の3行が削除されています。
// Allow reuse of recently-used addresses and ports.
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
// Allow reuse of recently-used addresses and ports.:これは、削除されるコード行の目的を説明するコメントです。syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1):この行が実際にSO_REUSEADDRオプションをソケットに設定していました。s: ソケットのハンドル。syscall.SOL_SOCKET: ソケットレベルのオプションを指定するための定数。syscall.SO_REUSEADDR: 設定するソケットオプションの名前。1: オプションの値をtrue(有効) に設定。
この変更により、Windows上でGoのネットワークパッケージがソケットを作成する際に、デフォルトで SO_REUSEADDR が設定されなくなります。これにより、前述のWindowsにおける SO_REUSEADDR の挙動に起因する問題が回避されます。SO_BROADCAST の設定は引き続き行われますが、これはブロードキャスト通信を許可するためのものであり、SO_REUSEADDR とは異なる目的を持ちます。
関連リンク
- Go Issue #2307: https://github.com/golang/go/issues/2307
- Go CL (Change List) 5306049: https://golang.org/cl/5306049
参考にした情報源リンク
- geeksforgeeks.org: https://www.geeksforgeeks.org/so_reuseaddr-socket-option-in-linux/
- liberatedsystems.co.uk: https://www.liberatedsystems.co.uk/linux-so_reuseaddr-and-so_reuseport/
- python.org: https://docs.python.org/3/library/socket.html#socket.socket.setsockopt
- winehq.org: https://wiki.winehq.org/SO_REUSEADDR
- stackoverflow.com: https://stackoverflow.com/questions/14388706/what-is-the-difference-between-so-reuseaddr-and-so-exclusiveaddruse
- ziggit.dev: https://ziggit.dev/p/so_reuseaddr-and-so_reuseport-explained
- github.com: https://github.com/golang/go/issues/2307