2001Yのプロフォール画像

2001Y@Y20010920T

Tailscale 越しの macOS 画面共有(VNC)を RDP に変換する備忘録

Windows の RDP クライアントから Tailscale 経由で Mac のデスクトップを使いたい、でも Mac 側で RDP サーバーを動かすのは面倒…という場合に、Linux サーバーを中継して VNC→RDP プロトコル変換を行う手順のメモ。

vnc2rdp という C 製のシンプルなプロキシを使い、低リソースで安定したブリッジを構築します。


構成

登場人物は 3 台。すべて同じ Tailnet に参加していることが前提です。

役割OSソフトウェアTailscale IP (例)備考
RDP クライアントWindowsmstsc.exe-最終的に操作する PC
Linux ブリッジUbuntu/Debianvnc2rdp, systemd100.106.19.34RDP(3389)を受け、VNC(5900)へ中継
Mac VNC サーバーmacOS画面共有100.103.59.97VNC を Listen

Linux ブリッジのセットアップ手順

Ubuntu 24.04 を例に、vnc2rdp のビルドからサービス化までを行います。

1. 依存パッケージのインストール

ビルドに必要なツールとライブラリを導入します。

sudo apt update
sudo apt install -y git build-essential cmake libssl-dev \
                    libjpeg-dev libpng-dev zlib1g-dev

2. vnc2rdp のビルドと配置

GitHub からソースを取得し、make install/usr/local/binに配置します。

git clone https://github.com/leeyiw/vnc2rdp.git ~/vnc2rdp
cd ~/vnc2rdp
# 安定版のv0.2.0をチェックアウト
git checkout v0.2.0

cmake .
make -j$(nproc)
sudo make install

3. MagicDNS から IP を動的に取得するスクリプト

vnc2rdpは接続先 VNC サーバーを IP アドレスでしか指定できません。Tailscale の MagicDNS 名を IP に解決するヘルパースクリプトを先に用意します。

/usr/local/sbin/vnc2rdp-pre.sh:

#!/usr/bin/env bash
# MagicDNS名を解決し、結果を一時ファイルに書き出す

# ★ここに対象MacのMagicDNS名を入れる★
DNS_NAME="mbp142021.taild89985.ts.net"

IP=$(getent hosts "$DNS_NAME" | awk '{print $1}' | head -n1)

if [ -z "$IP" ]; then
  echo "[vnc2rdp-pre] failed to resolve $DNS_NAME" >&2
  exit 1
fi

# systemdのEnvironmentFileとして読み込ませる
printf 'MAC_TS_IP=%s\n' "$IP" > /run/vnc-ip.env
exit 0

実行権限を付与:

sudo chmod +x /usr/local/sbin/vnc2rdp-pre.sh

4. systemd サービス化

永続的に稼働させるため、systemd のユニットファイルと、設定を上書きする Drop-in ファイルを作成します。

まず、ベースとなるユニットファイルを作成します。
/etc/systemd/system/vnc2rdp-tailscale.service:

[Unit]
Description=vnc2rdp bridge over Tailscale (Mac)
After=network-online.target tailscaled.service
Wants=network-online.target tailscaled.service

[Service]
# ここは空でも良いが、わかりやすさのため記載
ExecStart=/usr/local/bin/vnc2rdp

[Install]
WantedBy=multi-user.target

次に、具体的な挙動を定義する Drop-in ファイルを作成します。
/etc/systemd/system/vnc2rdp-tailscale.service.d/override.conf:

[Service]
# vnc2rdpプロセスはnobodyユーザーで実行
User=nobody
Group=nogroup

# ★ここにMacのVNCパスワードを入れる★
Environment="MAC_VNC_PASS=0426"
EnvironmentFile=-/run/vnc-ip.env

# ExecStartPreだけroot権限で実行し、/runへの書き込みを許可
PermissionsStartOnly=true
ExecStartPre=/usr/local/sbin/vnc2rdp-pre.sh

# 既存のExecStartをクリアし、動的に取得したIPで上書き
ExecStart=
ExecStart=/usr/local/bin/vnc2rdp -l 0.0.0.0:3389 ${MAC_TS_IP}:5900 -p ${MAC_VNC_PASS} -n

# サービス停止時に一時ファイルを削除
ExecStopPost=/bin/rm -f /run/vnc-ip.env
Restart=on-failure

-nオプションは、他の VNC クライアントが接続している場合にそれを切断して排他的に接続するモードです。macOS 標準 VNC との相性問題が減るため推奨です。

5. サービスの有効化と起動

sudo systemctl daemon-reload
sudo systemctl enable --now vnc2rdp-tailscale

# 起動確認
sudo systemctl status vnc2rdp-tailscale

active (running)となっていれば成功です。


トラブルシューティング

よくある問題とその対処法です。

症状原因と対策
RDP 接続後、画面が真っ黒VNC サーバー側の解像度が高すぎるのが原因です。
vnc2rdp が古い RDP プロトコル(4/5)にしか対応していないため、扱える解像度に上限があります。
対策: Mac の「システム設定 > ディスプレイ」で解像度を1920x10801680x1050程度に下げてください。Retina ディスプレイの場合は、スケーリング設定を「スペースを拡大」側に 1〜2 段階ずらすと改善します。
RDP エラー 0x500d上記の解像度問題と同じ原因です。クライアントがプロトコルエラーとして接続を切断しています。
サービスが failed で起動しないjournalctl -u vnc2rdp-tailscaleでログを確認。
Permission denied: PermissionsStartOnly=trueが設定されているか確認。
Bad format of server address: vnc2rdp-pre.shが IP を正しく取得できているか、getent hosts <MagicDNS名>で手動テスト。
VNC authentication failed: override.confMAC_VNC_PASSが正しいか確認。
RDP 接続自体ができないTailscale の接続確認: ブリッジサーバーから Mac へtailscale ping <MagicDNS名>が通るか確認。
ポートリスニング確認: ブリッジサーバーでss -ltn | grep 3389を実行し、vnc2rdp が待受中か確認。
Mac のスリープMac がスリープすると VNC サーバーも停止します。「システム設定 > 省エネルギー」でスリープを無効化するのがおすすめです。