DollsKit

SSH トンネルとリモートデスクトップ、Wake on LAN

各種状態の確認

Windows はとりあえず ipconfig、Linux は ifconfig と見せかけて最近は ip status。 仮想ネットワークドライバが入っていると数が多くなって紛らわしいが、 イーサネット eth のような名前のものが有線 (ethernet)、 Wireless LAN wlan のような名前のものが多い。

デフォルトゲートウェイと書かれているアドレスがおそらく回線業者からもらった モデムルータハブとか色々が一体になった機器 (何て呼べばいいんだ) の可能性が高い。 http://192.168.0.1 だいたいこんな感じのアドレスをブラウザに入力すれば 設定画面が使える可能性が高い。

https://www.cman.jp/network/support/
いつもの。 グローバルIPアドレス確認とポートチェックは欲しくなることが多い。

ローカル IP アドレスの固定

うまいことやったら無しでもできる気もするが、

最近は全部入りネットワークルータの DHCP 機能の中に、MAC アドレス一致による 固定アドレス割り当て機能を持つものも多く、あるならそちらの方が簡単かもしれない。 あらかじめ設定されたリスト内の MAC アドレスの機器が接続された場合、 DHCP からあらかじめ設定された IP アドレスが降ってくるようになる。

無い場合は OS 側の設定で DHCP 使用設定を切って固定 IP 使用に切り替える。

DHCP のアドレスレンジは設定画面を見るか、見なくても大体雰囲気で分かるので、 そこから若干遠めのアドレスを割り当てるとよい。

RPi へのポートフォワード設定

NAT 越え (エンジニア正式用語) またはポート開放 (ゲーマー向け怪しい用語)と 呼ばれているもの。 NAT (Network Address Translation; ローカル IP アドレスとグローバル IP アドレスとの変換) はネットワーク内→外の通信が最初に行われる場合 (普通に web ページを見るときなど) は うまくいくが、その逆 (サーバとして待ち受けるのが最初) の場合は特殊な設定なしでは うまくいかない。

外のインターネット、つまりルータの外 (WAN; Wide Area Network) からいきなり グローバル IP アドレス宛てにパケットが届いた場合、 ネットワーク内 (LAN) のどのノードに転送すればよいのかが分からないためである。 (内→外のパケットが最初の場合、ルータは送信元 IP アドレス + ポートを付け替えつつ それを覚えておけば、外→内のパケットを誰に向かって転送すればよいのかが分かる)

つまり、外から来たパケットの条件と転送先からなるエントリを先に追加しておけば解決できる。 設定画面では ポートフォワーディング という名前で書かれていることが多い、と思う。

TCP or UDP パケットは

が含まれており、ルータの WAN 側に届いたということは宛先アドレスは確定していることに なるので、その他3つについての条件を設定することになる。

転送がうまくいっているか確認するためには何らかの TCP サーバプログラムを 実行する必要がある (TCP ならコネクションの確立のために 3-way ハンドシェイクをする必要があるので それがうまくいくかどうかだけで判定はできる)。 最近は Python をインストールして python -m http.server とするのが 一番簡単なのかもしれない。 当然センシティブなファイルをインターネットに公開しないよう注意!

WAN 側アドレス変動への対処

インターネットプロバイダにもよるが、固定 IP アドレスは普通割り当てられない。 個人プランに無かったり、少なくともほぼ追加料金がかかる。

人形を設置する + DDNS

Raspberry Pi 4 で 50 円/月 程度という記事が多い。 当然 5 ではもっと上がるだろうし負荷によっても変わる。 しかし大きめに見積もっても 100-200 円/月 程度ではないだろうか。

グローバル IP アドレスの変動には DDNS (Dynamic DNS) が有効である。 定期的に DDNS サービスを展開するサーバに HTTP リクエスト (+認証) を送れば ドメイン名の解決先を送信元アドレスに更新してくれる。 これで固定のドメイン名が常に自分の家に解決されるようになる。 アドレスが変わった瞬間は諦めよう。

とりあえず sshd (ssh サーバ) を設定し、そのポート番号へ ルータでのポートフォワードを行えば SSH トンネリングでネットワークの中から 好きなことができるようになる。

玄関サーバを借りる

今ドキは1台のサーバマシンを借りて好きにできるのは VPS (Virtual Private Server) と呼ばれているらしい。 (仮想化されており、物理的には1台のモンスターマシンを複数人で共用しているが 実質的にハードウェア + OS のレベルで一台専用マシンを借りるのと同等)

さくらの VPS の一番しょぼいプランで 600 円/月。 ほぼ固定 IP アドレスを買うためのプランと見なされているかもしれない。

リモートデスクトップ

Windows Pro 以上はリモートデスクトップを使うことができ、インターネット越しに デスクトップ画面を操作することができる。 接続元は Home でも OK。

接続先でリモートデスクトップを有効にする

設定 > システム > リモートデスクトップ をオンにする。

罠: パスワードログインを1回以上行う

最近の Windows は2段階認証とパスコードでログインでき、パスワード認証を 使わなくて済む (非推奨として隠されているのをひっぺがさないと出てこない) ようになっているが、最低一度はパスワードログインを行ったことがないと リモートデスクトップの認証が通らない。 しかもパスワードが間違っているのとエラー内容の見分けがつかない。 これはひどい。

LAN 内で確認

Windows PC をもう一台 (Home でよい) 用意し、スタートメニューから “リモートデスクトップ接続” を起動する。

コンピューターのところには IP アドレスまたはホスト名を入力する。 接続ボタンを押して反応があれば相手に TCP が通っている。 ポートは TCP 3389 番。

認証ダイアログが出てきたら、 ユーザー名のところは “ドメイン名\ユーザー名” を入力する。 Windows のグループとかドメインとかはよく分からないが、 whoami コマンドで出てくる文字列をそのまま写せば OK。

SSH トンネリングして外のインターネットからリモートデスクトップ

一応この状態で TCP 3389 番に向かってポートフォワードを行えば家の外から 接続できるようになるが、当然のごとく外は攻撃パケットで溢れかえっている上に パスワード認証だけで PC を好きに操作できてしまうのはやばすぎるのでやめておこう。 一応証明書認証にするとかできるみたいだけど Windows は難しいし…。

人形に ssh が通っていれば、人形から家の中のネットワークにコネクションを張って ssh での安全な経路を経由して TCP コネクションを中継できる (SSH トンネリング)。 WSL2 だと仮想マシンの中から見える仮想ネットワークデバイスとのなんやかんやが面倒なので、 Windows 上で ssh コマンドを実行した方が無難。 こんなこともあろうかと、最近の Windows にはデフォルトで openssh が インストールされている。

> where ssh
C:\Windows\System32\OpenSSH\ssh.exe

通常の接続コマンドは以下の通りだが、 ~/.ssh/config で一連の設定を名前1つで呼び出し可能にできる (Windows ssh での場所は %HOMEPATH%/.ssh/config)。 要は通常 C:\Users\<user_name>\.ssh\ にある。

ssh (user@(default=現在のユーザ名))(IP アドレスやドメイン名) \
  -p (default=22) -i (default=~/.ssh/id_rsa)
Host shanghai4-remote (ssh コマンドに渡す好きな名前)
HostName yappy.mydns.jp
User (login user name)
Port 56789
# .ssh/config を書けばこれだけで OK
ssh shanghai4-remote

それに加えて以下を指定すると SSH トンネリング (ポートフォワーディング) を使用できる。

# 他のオプションに加えて
-L [このPCのポート]:[SSH サーバから見た接続先]:[そのポート] -N

そのままだと ssh のシェルも有効でコマンドを実行できてしまうが、 通常はポートフォワードだけできればよいので -N をつければシェルが無効になる。

初めてだと何をやっているのか混乱すること請け合いだし、指定の順番が覚えられないことに 定評があるが、WSL2 から ssh.exe で Windows 版を起動できるので シェルスクリプトにでもしておけばいいと思うよ。

  1. まず、ポートフォワード以外のオプションにより、普段の SSH コネクションと同じ方法で 接続先との安全な通信路が確立される。 秘密鍵公開鍵による認証 (相手がなりすましでないことの確認) と 通信路の暗号化による盗聴の防止ができる。 設定次第ではパスワード認証も可能だけど。。
  2. -L オプションの最初に指定するのはローカル PC (自分自身) のポート番号である。 つまり、この ssh コマンドは localhost (127.0.0.1) のこのポート番号で接続を待つ TCP サーバとして稼働を開始する。
  3. この通信路を使用したい場合、127.0.0.1:(port) に対して TCP connect すればよい。
  4. このポートに接続があった場合、SSH クライアントはすでに確立している 安全な通信路を通して SSH サーバにコネクション要求を転送する。
  5. これを受けたリモートの SSH サーバは -L オプションの2番目と最後で指定した アドレスとポートで TCP connect を行う。つまり、家の中のローカル IP アドレスに対して 接続できる。
  6. リモート SSH サーバからの TCP connect が成功したらそれを安全な通信路越しに SSH クライアントへ戻し、SSH クライアントは localhost(127.0.0.1) に対する TCP connect を accept し、TCP connection を確立させる。
  7. 要は TCP connect のフォワーディングである。
  8. 以後、send/recv/close 等もすべてフォワードされる。

ssh yappy@yappy.mydns.jp -p [port] -L 3389:192.168.1.200:3389 -N

リモートからの音が出ない

PC を起こす

リモート接続のために 24 時間 PC をつけっぱなしにするのは憚られる場合、 Wake on LAN (WoL) というネットワーク越しに起動させる技術が使える。 要はスリープ状態でもネットワークカードに通電しておき、 特殊なパケットに反応して本体を起動させる仕組みである。

性質上、深い眠りから覚ます方が深い設定が必要で面倒である。 それと最近の Windows は色々な電源状態が増えていて危険。

ハードウェアおよびそのファームウェアバージョンにも依存するので、 とにかく実際にやってみて挙動を確認することが重要。

ACPI State Windows Software WoL Hardware WoL
S0x Modern Standby スリープ ? X
S3 スリープ O X
S4 休止状態 O X
S4 シャットダウン (高速スタートアップ)(ハイブリッドシャットダウン) X ?
S5 完全シャットダウン X O

現在のハードウェア構成で対応している電源状態は powercfg コマンドで確認できる。

> powercfg /a
以下のスリープ状態がこのシステムで利用可能です:
    スタンバイ (S3)
    休止状態
    高速スタートアップ

以下のスリープ状態はこのシステムでは利用できません:
    スタンバイ (S1)
        システム ファームウェアはこのスタンバイ状態をサポートしていません。

    スタンバイ (S2)
        システム ファームウェアはこのスタンバイ状態をサポートしていません。

    スタンバイ (S0 低電力アイドル)
        システム ファームウェアはこのスタンバイ状態をサポートしていません。

    ハイブリッド スリープ
        ハイパーバイザーはこのスタンバイ状態をサポートしていません。

ソフトウェア WoL

Windows が行うタイプの WoL。 Windows 上で設定できる。

ハードウェア WoL

Windows が動けない状態で、動けるネットワークデバイスにやってもらう方法。 BIOS での設定が必要。

(未検証)

Magic Packet の送信

# yum だと net-tools らしい
apt install wakeonlan

wakeonlan 12:34:56:78:ab:cd

対象のネットワークデバイスがいるネットワーク内で MAC アドレス指定でコマンドを打つだけ。

Magic Packet 詳細

FF FF FF FF FF FF
[ MAC ADDR      ] x16

# 6 x 17 = 102 byte

イーサフレーム中のどこかにこのパターンが含まれればよいらしい。 ネットワークドライバに手を加えれば綺麗なイーサフレームを作れるかもしれないが、 無駄に面倒なので、対象機器の MAC アドレスから作ったデータを UDP のペイロードに置き、UDP ブロードキャストアドレスに送信すればよい。

実際に書こうとするとポート番号どうしようとなるが、何でもいい。 …とはいえ他の通信を妨害するのもアレなので、 well-known port 7 (ECHO) や port 9 (DISCARD) がよく使われるようだ。 古代のもはや使われていないプロトコルの番号である。

apt wakeonlan の実装 (Perl): https://github.com/jpoliv/wakeonlan/blob/master/wakeonlan

#include <netdb.h>

struct servent *
getservbyname(const char *name, const char *proto);
struct servent *
getservbyport(int port, const char *proto);

/etc/services からエントリを検索してくれる libc 関数らしい。 知らねえ。 ファイルには well-known ポートのリストが書かれている。 getservbyname("discard", "udp") でポート番号 9 が得られるらしい。 マジックナンバーでよくね。

# /etc/services
tcpmux          1/tcp                           # TCP port service multiplexer
echo            7/tcp
echo            7/udp
discard         9/tcp           sink null
discard         9/udp           sink null
systat          11/tcp          users
daytime         13/tcp
daytime         13/udp
netstat         15/tcp
qotd            17/tcp          quote
chargen         19/tcp          ttytst source
chargen         19/udp          ttytst source
ftp-data        20/tcp
ftp             21/tcp
fsp             21/udp          fspd
ssh             22/tcp                          # SSH Remote Login Protocol
telnet          23/tcp
smtp            25/tcp          mail
...