Tech Racho エンジニアの「?」を「!」に。
  • インフラ

RustDeskをGCP無料枠で中継サーバを立てて使ってみた

🔗 はじめに

日々Flutter情報を集めている中で偶然、リモートデスクトップソフトウェアのRustDesk

rustdesk/rustdesk - GitHub

がしばらく前からクライアントアプリに全面的にFlutterを使用していることを知りました。

RustDeskは「中継サーバ」を自分で立てて使う事が出来るようなので、今回は試しにGoogle Cloud
Platformの「無料枠」の範囲で中継サーバを立ててRustDeskを使ってみようと思います。
(※本記事は結果的にあまりFlutter感がありません、ご了承ください)

🔗 サーバの準備

RustDeskの「中継サーバ」を立てるには以下の番号のポートによる通信が必要なため、

  • tcp: 21115-21119
  • udp: 21116

それが出来るサーバ設置環境、そしてなるべくお安く試せるもの...ということで、一定の条件内で無料で仮想マシンを作ることが出来るGoogle Cloud Platform(GCP)のサービスであるCompute Engineで行うことにしました。

🔗 VM作り

まずはWebコンソールから無料枠の範囲で仮想マシン(VM)を作成します。
作成にあたってはこちらの記事

をとても参考にさせて頂きました🙏
OSは個人的な好みでDebian 12(bookworm)を選んでいます。

🔗 ファイアウォール設定

VM作成後、前述のポート番号(21115-21119)がpublicに通信可能になるようにファイアウォールを設定しました。

ファイアウォール ルールの詳細

ネットワーク タグ

🔗 静的外部IPアドレス

無料枠内でも出来そうということで、特に頻繁に再起動するつもりもないですが静的外部 IP アドレスを予約するに従ってアドレスを割り当てました。

なお私はドメインを持っていないので今回この中継サーバはIPアドレス指定で使うことにします。

🔗 スワップ追加

無料枠VMのe2-microはメモリが1GBしかないので、スワップファイルを作って使うことにしました。

dd if=/dev/zero of=/swapfile.bin bs=1M count=4096
mkswap /swapfile.bin
chmod 0600 /swapfile.bin
swapon /swapfile.bin

/etc/fstab に以下の行を追加しておきます。

/swapfile.bin none swap defaults 0 0

🔗 Docker Engineインストール

RustDeskサーバのコンテナを動かす(後述)ために、Docker Engineをインストールしました。

DebianリポジトリにもDocker Engineはありますが、現行バージョンのものを使うため公式サイトの手順を用いて download.docker.com のものをインストールしました。

🔗 RustDesk Serverを動かす

RustDesk ServerはOSS版とPro版がありますが、今回はOSS版を使います。

OSS版のリポジトリ

rustdesk/rustdesk-server - GitHub

にはいくつかのインストール手順が紹介されており、また「rustdesk」で検索すると見つかる以下の記事では(当時のバージョンの)サーバソフトウェアを直接OSにインストールしていますが、

今回はClassic imageとして紹介されているコンテナを2個動かす方法にしました。
(※S6-overlay based imagesS6-overlay に馴染みがなかったもので……)

You can also use docker-compose, using this configuration as a template:

と紹介されているテンプレートをほぼそのままコピーした compose.yaml を用意し、 docker compose up -d で起動します。

# https://github.com/rustdesk/rustdesk-server#classic-image

version: '3'

networks:
  rustdesk-net:
    external: false

services:
  hbbs:
    container_name: hbbs
    ports:
      - 21115:21115
      - 21116:21116
      - 21116:21116/udp
      - 21118:21118
    image: rustdesk/rustdesk-server:latest
    # 元は command: hbbs -r rustdesk.example.com:21117 のようになっているが
    # ドメイン未使用なので -r オプション割愛
    command: hbbs
    volumes:
      - ./data:/root
    networks:
      - rustdesk-net
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    ports:
      - 21117:21117
      - 21119:21119
    image: rustdesk/rustdesk-server:latest
    command: hbbr
    volumes:
      - ./data:/root
    networks:
      - rustdesk-net
    restart: unless-stopped

🔗 クライアントアプリ

……ということで中継サーバを準備したので手元の各マシンにプラットフォームに応じたクライアントアプリを入れ、実際に使ってみました。

🔗 ビルド済みアプリ

ビルド済みのアプリはGitHubのリリースページ

にて配布されています。

🔗 GUIでセットアップ

普段から画面のある環境で使用しているGUIのあるマシン(手元ではWindowsやMacが該当)についてはGUIでアプリのインストールを行いました。

クライアントアプリを起動し、「認証・中継サーバー」の設定に外部静的IPアドレスで取得した中継サーバのIPアドレスを設定します。

「認証・中継サーバー」の設定

Home画面に戻った後、下部のステータスバーに「準備完了」と出れば中継サーバへの接続成功です。

準備完了

Home画面にはアプリインストール時に生成されたそのマシンのIDが表示されており(再生成もできます)、他のマシンのアプリからはそのIDを指定して接続します。
またHome画面には「ワンタイムパスワード」も表示されておりそれを使って接続することも出来ますが一度使うと変わってしまうので、リモートマシンの画面を見ずに接続したい場合は「固定のパスワード」を設定して使います。

パスワード設定

🔗 Linuxクライアント: GUIを使わずにセットアップ

GUI環境は整えてあるが普段はモニタを繋いでおらずSSH接続で使う、というLinuxマシン(Debian 12)が手元にあり、そこにもRustDeskを入れてみました。

アプリパッケージ(rustdesk-1.2.3-x86_64.deb)は別のマシンでブラウザからダウンロードしSFTPで送り込んでおき、インストールと設定を以下のようにしてGUIを使わずに行いました。

# gdebiを使ってrustdeskのdebをインストール
sudo apt update
sudo apt install gdebi-core
sudo gdebi -n rustdesk-1.2.3-x86_64.deb

# --get-idオプションを使うとIDが表示されるのでメモしておく
sudo rustdesk --get-id

# 固定パスワードを設定
sudo rustdesk --password <固定パスワード>

# 中継サーバーのアドレスを設定
sudo rustdesk --option relay-server xxx.xxx.xxx.xxx
sudo rustdesk --option custom-rendezvous-server xxx.xxx.xxx.xxx

# おまけ: 権限をFull accessに設定
# リモート接続でRustDeskのGUIアプリを操作して設定変更できるようになる
sudo rustdesk --option access-mode 'full'

# rustdeskのサービスを再起動
sudo systemctl restart rustdesk.service

なお、元より画面がないHeadless環境については「ダミー画面」を用いる手順がWikiに紹介されていました。

🔗 クライアントアプリをビルドしてみる

さて、ここまで中継サーバとビルド済みクライアントアプリをセットアップして普通に使うことは出来るようになったので、次はFlutter製であるクライアントアプリについて、ひとつソースからビルドしデバッグ実行してみようと思います。

🔗 macOSでのビルド

以下、当時のmasterブランチ先頭をmacOS(Sonoma 14.1.2)上でビルドに成功した時の大まかな手順をご紹介します。

どうも現状(2023/12)Flutter版の詳しいビルド手順はまとめられておらず、README.mdドキュメントサイトで読める手順(例えばmacOS用)はFlutter以前(Sciter)向けのもののようでした。

そのため、以下の手順は bridge.ymlflutter-build.ymlなどCIの設定ファイルから手順を読み取り独自にまとめたものとなります、ご了承ください。

🔗 Flutter/HomeBrew/Rust/Xcode

各種SDKやツールをインストールします。

🔗 brew install

Install build runtimeを参考に、以下のツールを brew install します。

brew install llvm create-dmg nasm cmake gcc wget ninja pkg-config

🔗 vcpkg

Install vcpkg dependenciesや、「vcpkg version: 2023.10.19」の記述を参考に、vcpkg の準備とパッケージのインストールを行います。

# 好みの場所にvcpkgをclone
git clone --depth 1 -b 2023.10.19 https://github.com/microsoft/vcpkg.git

cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg install libvpx libyuv opus aom

vcpkg installに成功したら、vcpkgディレクトリがお使いのシェルに環境変数VCPKG_ROOTとしてエクスポートされるように設定します。

# 例: ~/vcpkgにcloneした場合
export VCPKG_ROOT="${HOME}/vcpkg"

🔗 ソースコード準備

git clone https://github.com/rustdesk/rustdesk.git

# 当時のコミット
# git checkout 8452a17d79bc5111453e34ac84ada109d11f940e

🔗 flutter_rust_bridgeでbindingコード生成

cd rustdesk/flutter
flutter pub get

# flutter_rust_bridgeでbindingコード生成
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ../src/flutter_ffi.rs --dart-output ./lib/generated_bridge.dart --c-output ./macos/Runner/bridge_generated.h

🔗 cargo build

# liblibrustdesk.dylibビルド
cargo build --features "flutter,flutter_texture_render"

この時、もしHomeBrewでbinutilsがインストールされているなどの理由でarranlibがApple製ではないものにパスが通ってしまっている場合は、Apple製のものが使われるように対策する必要があります。
(一時的に/usr/local/binを含まないPATHをexportしたり、いっそbinutilsアンインストールでも?)

ビルドに成功すると rustdesk/target/debug/liblibrustdesk.dylib が作られます。

🔗 アプリの実行

ここまで成功したらflutter run -d macosや、rustdesk/flutter/を開いたVS Code上から実行することが出来ます。

VS Code上で実行し適当なLinuxデスクトップに接続しつつブレークポイントで止めてみました。

デバッグビルドを実行

デバッグ実行してわかりましたが設定画面で一部文字が入り切っておらず例の「工事中」出てますね🚧
(Debugバナーは非表示に設定されていました)

🔗 気になる技術要素/ライブラリ

ということで、クライアントアプリをソースからビルドし無事Flutter製であることが確認できました……が、RustDeskはそこそこの規模のコードなこともあってこれ以上の深掘りについては難しく、pubspec.yamlも眺めつつ「へぇ〜こんなの使ってるのか」と思う程度となってしまいました、何卒ご容赦ください🙇‍♂

🔗 flutter_rust_bridge

  • そもそも「RustDesk」であり、中継サーバもRustで出来ていますがクライアントアプリでもRustがガッツリ使われています。中核となるリモートデスクトップ機能はRustで出来ており、Dart(Flutter)側との連携にflutter_rust_bridgeが用いられています。

🔗 texture_rgba_renderer

  • 「This plugin is originally developed for RustDesk.」とあり、Rustで描画されるリモートデスクトップ画面をFlutterアプリ内に表示するために作られたもののようです。

🔗 dash_chat_2

  • チャット機能の実装にdash_chat_2をforkして使っているようです。

🔗 window_manager / desktop_multi_window

  • それぞれwindow_managerのforkと、pub.devにあるdesktop_multi_windowforkしたもののforkのようです。
    Flutter本体でのデスクトップOSサポートが今後どれくらい進むかわかりませんが、現時点では細かなウィンドウ管理などにはこれらプラグインが必要という事なのでしょう。

🔗 おわりに

以上、(最後が少しすぼみ気味ですが)RustDeskを使ってみた、となります。

9月にGoogle EarthがWeb/iOS/Android共通でFlutter化したりと、iOS/Androidアプリ以外での用途も少しずつ増えて来たように思います。
今後FlutterがどのようにWebやデスクトップ用途に広がっていくのか、RustDeskを使いながら見守っていきたいと思います。



CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。