Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 に相当するオプションはありません。

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 を無条件に設定することは、以下のような問題を引き起こす可能性があります。

  1. ポートの乗っ取り: 悪意のある、または単に設定ミスのある別のアプリケーションが、既にGoアプリケーションが使用しているポートにバインドできてしまう可能性があります。これにより、正当なサービスが妨害されたり、データが傍受されたりするリスクが生じます。
  2. 予期せぬ動作: 開発者が意図しない形で、複数のアプリケーションが同じポートを共有してしまうことで、デバッグが困難な問題や、サービスの不安定化を招く可能性があります。
  3. セキュリティリスク: 特に、特権の低いプロセスが特権の高いプロセスが使用しているポートを乗っ取ることが可能になる場合、深刻なセキュリティ脆弱性につながります。

このコミットは、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 とは異なる目的を持ちます。

関連リンク

参考にした情報源リンク