背景
- ローカルのつよつよ PC で StyleGan とかで画像処理するサービスのデモを構築し提供したい(ベータサービス公開みたいな)
- Solana validator などのブロックチェーン関連のサーバーを建てたい(これもつよつよ CPU + GPU がいる)
- ローカル環境(e.g. 自宅, オフィス)ではグローバル IP は無い
- ネットワーク帯域はそれほど消費しない & 速度(レイテンシ)も重要ではない
- AWS とかでつよつよ構成のインスタンス立てると高すぎる & 常時使うわけではないので無駄が多い
外部から http://my-awsome-ml.com:8088
にアクセスがあったら, それをローカルの PC に転送し, 処理をして結果を返すのをやりたい.
reverse ssh 接続でポートフォワーディング(トンネリング)で実現します.
ルータに変更などは不要ですが, ゲートウェイ(踏み台)として global IP(or dynamic DNS)のある VPS などは必要になります.
環境
- local PC
- VPS など global IP(or dynamic dns) があるサーバ. ネットワークを転送するだけなので, 月数百円とかのレベルのでよい
手順
- firewall のポートを開ける
- reverse SSH でローカル PC からポートフォワーディングの指定をする
- ただし reverse SSH 接続だけでは外部からはアクセスできないので, sshd config を編集して外部からもアクセスできるようにする
詳細
ポート開放
まず, ゲートウェイ(踏み台)サーバでポートを開放します.
Centos7 でたとえば 8088~8098 まで開放したい場合,
# server: my-awesome-ml.com
> sudo firewall-cmd --zone=public --add-port=8088-8098/tcp --permanent
> sudo firewall-cmd --reload
として開放します.
reverse SSH
How does reverse SSH tunneling work?
https://unix.stackexchange.com/questions/46235/how-does-reverse-ssh-tunneling-work
VPNを使わずに、逆SSHポートフォワードを使って自宅のマシンをRDPで外から操作する
https://qiita.com/mikuta0407/items/b1a3b94b356e97eafb8e
ありがとうございます.
ローカル PC から,
# [local]
> ssh -R my-awesome-ml.com:8088:localhost:8088 -N my-awsome-ml.com
のようにしてコネクションを張ります.
(@ttdoda さまからご指摘いただきました. ありがとうございます)
特にエラーがなければ成功で, 何も表示されずそのまま動いています.
sshd の変更
ただ, この状態ですとサーバーの my-awesome-ml.com
内部から localhost:8088 にアクセスすればローカル PC に転送されますが, 外部(インターネット)から my-awesome-ml.com:8088
でアクセスはできません.
sshd の設定で GatewayPorts yes
にすることで, 外部からの my-awesome-ml.com:8088
のアクセスが可能になります.
GatewayPorts clientspecified
にする必要がありました. @ttdoda さまご指摘ありがございます
socat を使う
sshd に変更をいれるのを躊躇われる場合は, socat でフォワーディング(リレー)することでもいけました.
サーバー側で
# server: my-awesome-ml.com
$ socat TCP4-LISTEN:8088,fork TCP4:localhost:8089
のようにします. reuseaddr
をお好みで添えて.
同じポート番号にしてしまうと ssh reverse portforwarding のプロセスとポートが衝突して Address already in use になってしまうので注意です.
上記のように socat でリレーする場合は reverse ssh で接続するポートも 8089 に変えておきます.
ローカル PC でサービスを立てる
あとはローカル PC でサービスを立てるだけです!
たとえば http サービスを建てて確認してみましょう.
$ python -m http.server 8088
これで http://my-awesome-ml.com:8088
にアクセスするとローカル PC でうごいている http サーバに転送されて処理されるはずです!
python http.server はそのままだとカレントディレクトリの中身を見せるので, ディレクトリの中身全世界に公開されてしまうので注意してくださいね.
Advanced な内容
https にする
いつものアプリケーションサーバに対して HTTPS でアクセス可能にする気軽な方法
https://qiita.com/cuzic/items/672c66e8ccdfba067166
socat で簡易プロキシサーバーを立てよう
http://racchai.hatenablog.com/?page=1460687400
ありがとうございます. socat との組み合わせでいけますね.
安定運用したい
Reverse ssh tunnel を安定運用する
https://qiita.com/syoyo/items/d31e9db6851dfee3ef82
複数ポートを転送したい
Solana の validator のように, 複数ポートを使うようなサービスも扱えるようにしたい.
ssh ポートフォワーディングで複数ポートを転送したいメモ
https://qiita.com/syoyo/items/5792de1404bf6fac5843
ワイルドカード指定とかはできないので, ポート数ぶん指定して reverse ssh 張る必要がありました.
socat のほうもポートごとにプロセスを立てる必要があります.
セキュリティはどうか
少なくともローカル PC で動かすサービスはある程度 Sandbox/Contaier 実行にしたほうがよいでしょう(e.g. c Docker とか LXC とか). 画像処理サービスの場合, 悪意あるユーザーが変に巨大な画像をアップロードしたり, JPG/PNG に悪意あるデータ(デコードすると buffer-overrun や out-of-memory を引き起こすなど)を忍ばせる可能性があります.
プログラムが CPU だけの場合は wasm-sandboxing でプログラム全体をセキュアにするという手もあります https://github.com/shravanrn/wasm-sandboxing
GPU を使う場合はでも完全にセキュアというわけにはいかないでしょう(Docker だと GPU device は privileged 権限で動かさないといけなかったような).
vGPU なりで HW できちんと仮想化されたのが理想ですが, その場合はサービスを本番運用したときに考えればよいでしょうか.
Solana validator を運用する場合は悩まなしいところですね.
(悪意ある validator が GPU をハングさせるような命令を忍ばせてくる可能性も無きにしも非ず)
VPN?
OpenVPN とかでローカル PC と踏み台サーバーをつないで通常の順方向 port forwarding(or proxy)という手もありますが, VPN だとネットワーク環境全体を設定することになるので, セキュリティ的にちょっと気になります(侵入とかがあったら環境丸見え).
TODO
- proxy server(e.g. http server の proxy 機能)を使ったほうがいいか?
- socat 以外で, ポートフォワーディングだけ処理してくれるなんかいい感じに処理してくれそうなツールがありそうなので探してみる
- ポートフォワーディングだけ処理してくれるサービスがありそうだが...(悪用されやすいのか, 需要がないのか, ぱぱっとは見つからなかった)
- ローカル PC から socat で SSL + reverse proxy する方法を試す(サーバ側で socat 実行が不要にできるように).
- reverse ssh ポートフォワーディングのセキュリティについて考える(数万円くらいでオンデマンドセキュリティチェックしてくれるサービスほしい)