Chinachu / Mirakurun

A Modern DVR Tuner Server for Japanese TV.
https://chinachu.moe
Apache License 2.0
626 stars 101 forks source link

unixドメインソケットからはRPCがつかえない [BUG] #108

Closed hinaloe closed 3 years ago

hinaloe commented 3 years ago

Environment (再現している環境)

mirakurunを適切にアクセス元を管理したnginxの裏に、デフォルトのdocker-compose.ymlで利用できる unix:/usr/local/mirakurun/run/mirakurun.sockへリバプロさせる運用をおこなっています。最小限のnginxのconf(serverディレクティブ)は最後に示します。

Issue (不具合の内容)

上記のようにunixドメインソケットを利用環境において、3.9.0-betaより導入されたWebSocket RPCを利用しようとすると403エラーが返されてしまい、接続ができません。これにより3.8までは問題のなかった同環境においてWebUIが表示できなくなってしまっています。

なお、当然ながらIPアドレスを利用した直アクセスではこの問題は発生しません。

Expected (期待される動作)

unixドメインソケット経由でもWebSocket RPC が利用できる

簡易的には同マシン上にて以下がいずれも同様に利用できる

$ npx wscat --connect ws://localhost:40772/rpc
$ npx wscat --connect ws+unix:///usr/local/mirakurun/run/mirakurun.sock:/rpc

原因

https://github.com/Chinachu/Mirakurun/issues/69#issuecomment-651030251 で示されているように、Mirakurunでは外部からの意図しないアクセスを防ぐためアクセスにかなり気をつかっていると把握しています。

RPCエンドポイントにおいてもアクセス元とオリジンを検証しているようです。

https://github.com/Chinachu/Mirakurun/blob/35e832e8dc09bbdf3d7e032b94837580bb462929/src/Mirakurun/rpc.ts#L126-L130

直接TCPソケットからのアクセスであればreq.socket.remoteAddressでクライアントのアドレスが取得できますが、 unixドメインソケットを使用している場合は req.socket.remoteAddress req.socket.localAddressなどはundefinedとなります。これによりip.isPrivateの検証が失敗し、適切なアクセスでも403が返されてしまうようです。 この部分をスキップさせると問題なくWebUIが表示できます。

(参考としてreq.socket.server._pipeNamereq.socket.server.address()ではunix domain socketの識別が可能なようです)

また、同様にip.isPrivateを使用している

https://github.com/Chinachu/Mirakurun/blob/35e832e8dc09bbdf3d7e032b94837580bb462929/src/Mirakurun/Server.ts#L109-L110

では、アドレスのあることを確認しているようです。同様の条件でかまわないのであればそのチェックだけ挟んでもらえればよいかと思います。

(余談ですが、うちではIPv6アクセスに際してNW構成の関係上fe80::/10ではないグローバルなアドレス空間をLAN内においても利用しています。この場合、ip.isPrivateだと範囲外のためアクセスできなくなるようです。)

付録: 再現可能な最低限のnginxのconf(serverディレクティブ)

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name example.com;

    add_header Referrer-Policy same-origin always;
    etag off;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    location / {
            proxy_pass http://unix:/usr/local/mirakurun/run/mirakurun.sock;
    }

}
kanreisa commented 3 years ago

修正しました。ご報告ありがとうございました。