Xpra(Xアプリリモートビューワー)について
XpraというリモートのXアプリを表示できるパッケージについて紹介します。
概要
特徴
- アプリの画面だけをシームレスに表示できる(RDPやVNCとは違う)
- youtubeが普通に見れるくらい転送速度がはやい(X11Forward over SSHとは違う)
- プロトコルが多様(TCP/UDP/SSH/SSL/WS/WSS/HTTP/HTTPS)
- ブラウザからアクセスできる!
微妙な点
- ストレージを食う(Ubuntu Serverに入れると1GBくらい増加)
- 音が出なそう(音が出るはずだが出ない)
- Fcitxで日本語入力できなさそう
ざっくりな使い方
リモート・ローカルともにパッケージをインストール
sudo apt install -y xpra python-cryptography gedit # geditはテスト用
ローカル
sudo apt install -y xpra python-cryptography
リモート
Xwrapper.configでallowed_usersをconsole -> anybodyに変更
allowed_users=anybody
ローカル
xpra start ssh:REMOTE --start-child=gedit
※REMOTEは~/.ssh/configに追加しておいてください。
これでリモートで実行したgeditがローカルPCに表示されます。個人的にはローカルのgeditを使ってるのと変わりません。
背景
リモートホスト上のDockerコンテナで稼働するGUIアプリをローカルホストの画面に表示したくて、X11Forwarding over SSHを試したが実用に耐えませんでした。
[ec2-user@ip-10-0-100-159 ~]$ sudo yum install xorg-x11-xauth xeyes
[ec2-user@ip-10-0-100-159 ~]$ xeyes
EC2上のAmazon Linuxにssh -XC
で接続してアプリを起動しても、まともに使えない。エラーにならないのはxeyesくらいで、firefoxはもちろんgeditですら使えない。圧縮方式のチューニングなどがあるようだが、RC4を使うなど現実的ではないので全く別の策を探していた。
その理由について、Super Userに丁寧に解説してくれている人がいました。
The X11 protocol was never meant to handle graphically (in terms of bitmaps/textures) intensive operations. Back in the day when X11 was first designed computer graphics were a lot simpler than they are today.
……
This is very efficient if the display to be rendered consists of a limited number of simple graphical shapes and only a low refresh frequency (no animations and such) is needed. Which was the case back in the days when X11 was first developed.
……
Other protocols (like RDP and VNC) are more designed to let the remote system do all the hard work and let that system decide which updates to send to the client (as compressed bitmaps) as efficiently as possible. Often that turns out to be more efficient for modern GUI's.
要は、X11は描画命令を扱うため昔のコンピューターには適していたが、現代によく使われるブラウザみたいな画像処理を行うのには向いていないということらしいです。
Xpra
ドキュメント
man xpra
と以下の公式ドキュメントが参考になりました。
サポートプロトコル
ハンズオンの前にざっくり要素を見ておきます。
エンコード
- png
- png/P
- png/L
- rgb
- webp
- jpeg
- vp8
- vp9
- h264
- h265
指定しないと自動で選択されます。依存関係でffmpeg等も入るので、筆者環境ではh264が選択されていました。
通信プロトコル
- tcp
- udp
- ws_ウェブソケット
- wss_セキュアウェブソケット
- ssl
- ssh
...
※この中でsshだけは別枠扱いで、sshを指定するだけで認証と暗号化を兼ねられます。
暗号化
- ssl_公開鍵暗号
- AES_共通鍵暗号
認証
- env_環境変数でパスワードを設定
- password_コマンド上でパスワードを指定
- file_ファイルにパスワードを指定
...他にもldapやら
http/httpsサポート
本体のモジュールとは別にxpra-html5というモジュールをインストールすれば、ブラウザでアクセスするだけで、アプリを開けます。
ハンズオン
準備
リモート・ローカルともにパッケージをインストール
※検証ではリモート側はaws ec2 ubuntu server 20.04 t3a.xlargeを利用
(geditくらいならmicroでも動くが、ブラウザ使おうとするとこの辺が必要)
リモート
sudo apt install -y xpra python-cryptography gedit firefox # gedit/firefoxはテスト用
ローカル
sudo apt install -y xpra python-cryptography
リモート
Xwrapper.configでallowed_usersをconsole -> anybodyに変更
allowed_users=anybody
起動
ローカル
xpra
コマンド単体で実行するとなんだかよくわからないGUIが表示されますが、今回は使いません。
※以下では、環境によって変わる変数をREMOTE,PASSWORDなど大文字で表現するので、適宜変更してください。
SSHでローカル側から起動
ローカル
xpra start ssh:REMOTE --start-child=gedit
geditが表示される
リモート側で事前に起動してローカルからSSHで接続
リモート
xpra start :1111
DISPLAY=:1111 gedit
:1111
はローカルホストの1111番ディスプレイという意味です。任意の数字を設定できます。DISPLAY=:1111 gedit
はローカルの1111番ディスプレイでgeditを起動という意味になります。
ローカル
xpra attach ssh:REMOTE:1111
※筆者環境では上記コマンドはなぜか受け付けられずxpra --ssh="ssh -i ~/.ssh/SSHKEY" attach ssh:USERNAME@REMOTEIP:1111
自前で鍵を指定するオプションを追加しています。
※ローカル側からアプリを起動する場合も、リモート側でセッション開始して待機する場合もstart
というコマンドを使うので、混同しやすいですので気をつけてください。
任意のTCPポート経由
リモート
xpra start --start=gedit --bind-tcp=0.0.0.0:10000
ローカル
xpra attach tcp://REMOTE:10000/
暗号化なし、認証なしです。tcp://REMOTE:10000/
の最後の/
がないとエラーになった気がします。
HTTP経由
リモート
git clone https://github.com/Xpra-org/xpra-html5
cd xpra-html5/
sudo ./setup.py install /usr/share/xpra/www
sudo apt install websockify
xpra start --start=gedit --bind-tcp=0.0.0.0:10000 --html=on
ローカル
ブラウザでURLを開いてください。
firefox http://REMOTEIP:10000/
なんと、ブラウザ内にgeditが起動されています。この機能だけならローカル側にはパッケージインストールの必要はありません。ブラウザだけあれば利用できます。
Desktop Session
RDPみたいなのができるらしいのですが、何なのよくわかりませんでしたが、一応やり方だけ。
ローカル
xpra start-desktop ssh:REMOTE
SSL経路経由
ここらからが本番です。TCPレイヤをSSLで経路暗号化します。これはxpraのネイティブプロトコルです。
下記を参考に証明書を作ります。
ローカル
./make_ip_cert.sh
cat REMOTEIP.key REMOTEIP.crt > ssl-cert.pem
上記で作成したssl-cert.pemをリモート側に移してください。
移したら証明書を指定してxpraを起動します。
リモート
xpra start --start=gedit --bind-tcp=0.0.0.0:10000 --ssl-cert=ssl-cert.pem
ローカル
ローカルでは、REMOTEIP.crtを指定してアタッチします。
xpra attach ssl://REMOTEIP:10000/ --ssl-ca-certs=REMOTEIP.crt
geditが開きます。Wiresharkで確認しましたが、TLSで暗号化されていました。
ローカルで証明書を指定しないと接続できません。
xpra attach ssl://REMOTEIP:10000/
これは認証失敗ではありません。証明書バリデーションエラーで弾かれています。
HTTPS経由
リモート
再掲ですがhtmlモジュールをインストールします。
git clone https://github.com/Xpra-org/xpra-html5
cd xpra-html5
./setup.py install /usr/share/xpra/www
xpra start --start=gedit --bind-tcp=0.0.0.0:6919 --ssl-cert=ssl-cert.pem
ローカル
firefox https://REMOTEIP:10000/
証明書エラーは出ますが、無理やり進めば、ブラウザ内にgeditが表示されます。
でも、
firefox http://REMOTE:10000/
でもアクセスできるからだめです。
状況によって勝手にfallbackされるので、htmlアクセスについて簡単にパターンを検証します。
- リモート側で証明書指定なし_httpでのみアクセス可能
- xpra-html5モジュールを削除_ブラウザでのアクセス不可
- bind-tcpをbind-sslに変更_ブラウザはhttpsのみ可能、CLIは証明書指定のみ可能
- bind-tcpをbind-sslに変更し--html=offを追加_証明書を指定したCLIのみアクセス可能
上記をまとめると暗号化して使いたい場合は、
リモート
xpra start --start=gedit --bind-ssl=0.0.0.0:10000 --ssl-cert=ssl-cert.pem
ローカル
xpra attach ssl://REMOTEIP:10000/ --ssl-ca-certs=REMOTEIP.crt
ブラウザサポートをやめたい場合--html=off
を追加もしくは、xpra-html5モジュールを削除。
認証
sslを指定すれば暗号化はされますが、接続クライアントの認証はされていないので誰でも接続できます。
パスワード認証
リモート
xpra start --start=gedit --bind-ssl=0.0.0.0:10000 --ssl-auth=password:value=PASSWORD --ssl-cert=ssl-cert.pem
ローカル
xpra attach ssl://:PASSWORD@REMOTEIP:10000/ --ssl-ca-certs=REMOTEIP.crt
当然ですが、ローカルでするパスワードが間違っていれば弾かれます。
ENV認証
環境変数を指定してパスワード設定するので、プロセス一覧に表示されません。
リモート
XPRA_PASSWORD=PASSWORD xpra start --start=gedit --bind-ssl=0.0.0.0:10000 --ssl-auth=env --ssl-cert=ssl-cert.pem
ローカル
xpra attach ssl://:PASSWORD@REMOTEIP:10000/ --ssl-ca-certs=REMOTEIP.crt
使うなら、ファイルか環境変数だと思いますが、取り回しを考えると環境変数がイイですかね。
また、それぞれの場合にブラウザでアクセスするとパスワードフォームが出てきます。
音声出力
色々やりましたが音を出すことができませんでした。
debugすると、下記エラーが出ていました。
missing ['mad'] from ('mpegaudioparse ! mad', None)
missing ['mad'] from ('mpegaudioparse ! mad', 'qtdemux')
とかが関係ありそうな気がします。
InputMethod
--input-method=METHOD
Specify which input method to configure. This sets a number of
environment variables which should be honoured by applications
started with the start-child option.
The following METHODs are currently supported:
none Disable input methods completely and prevent it from in‐
terfering with keyboard input. This is the default.
keep Keeps the environment unchanged. You are responsible for
ensuring it is correct.
xim Enables the X Input Method.
IBus Enables the Intelligent Input Bus.
SCIM Enables the Smart Common Input Method.
uim Enables the Universal Input Method.
man xpra
より引用
fcitxがありません。一応全部してしてみましたが、日本語入力できませんでした。今後追加調査予定です。
まとめ
もともとの目的であるリモートサーバで実行しているアプリをローカルマシンに表示することができそうです。HTTPSでセッション貼れるので、AWS FargateにALB経由でトンネル出来るんではないかと画策中です。DockerコンテナGUIアプリと組み合わせ、アプリをリモートやローカルに移し替えられるポータビリティを確保出来る仕組みを考えていきますので、興味があればご参照ください。
追記
xpra専用のコンテナとアプリコンテナを準備し、双方をボリューム共有でつなぎ、クライアントはxpraコンテナに接続するというアーキテクチャです。これなら、分割することでイメージの肥大化を防げるかもしれません。
Discussion