Cloudflare Zero Trust と Apache Guacamole を使ってセキュアにブラウザで RDP する

# 目的
[Apache Guacamole™](https://guacamole.apache.org/) を使ってみたかったので、以下のレポジトリを元ネタに試してみます。

https://github.com/RamboRogers/cloudflareguacamole

自分なりに修正を入れたものが以下のものになります。

https://github.com/kyouheicf/cloudflare-guacamole

上記の内容を使って、以下のような構成を組みます。

- Ref. [Implementation and architecture — Apache Guacamole Manual v1.5.5](https://guacamole.apache.org/doc/gug/guacamole-architecture.html)

```mermaid:
%%{init: {'themeCSS': 'img {width: 48px; height:48px;}'}}%%

graph TB
Eyeball[<img src='https://cf-icons.pages.dev/eye.svg' width='48' height='48'/><br>Eyeball]

subgraph Cloudflare
PublicHostname[Public Hostname<br>guacamole.yourdomain.com]

subgraph Access ["<div align=center>Access"]
  App[<img src='https://cf-icons.pages.dev/cloudflare-access.svg' width='48' height='48'/><br>Self-Hosted Application]
end

subgraph Tunnel
  TunnelName[Tunnel Name<br>guacamole]
end

end

subgraph Cloud
  subgraph Linux
    subgraph Container
      guacamole
      cloudflared[<img src='https://cf-icons.pages.dev/Cloudflared.svg' width='48' height='48'/><br>cloudflared]
      guacd
      cloudflared --> |http://127.0.0.1:8080|guacamole
      guacamole --> guacd
    end
  end
  guacd --> |rdp://10.x.x.x:3389|Windows[<img src='https://cf-icons.pages.dev/WindowsLaptop.svg' width='48' height='48'/><br>Windows]
end

Access --> |Authenticate|IdP[<img src='https://cf-icons.pages.dev/security-fingerprint-privacy.svg' width='48' height='48'/><br>Identity Provider]

Eyeball --> |Browser Access|PublicHostname
PublicHostname --> App
App --> |Route|TunnelName
TunnelName --> cloudflared
```

# 参考：ワカモレとは

https://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%AB%E3%83%A2%E3%83%AC

>ワカモレ（スペイン語: guacamole [wakaˈmole, ɡwakaˈmole]）とは、アボカドをメインに青唐辛子または唐辛子、トマト、玉ねぎ、コリアンダー、塩、レモンを加えて作られたメキシコ料理のサルサの一種である。ワカモレという名称の由来は、ナワトル語のAhuacamOlli=Ahuacatl（アボカド）+molli(ソース）からなる[1]。なお、片仮名表記では、グァカモレ、グワカモレ、グワカモーレ、ガカモーレ、グアカモーレ、ワカモーレ、グァカモーレ、グアカモレ、ガカモレと書かれることもあるが、本稿ではワカモレに統一する

# Docker インストール
クラウド上の Linux サーバーでコンテナとして Apache Guacamole を稼働させるために、Docker をインストールします。

```shell:
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io
```

サービスとして起動します。

```shell:
$ docker -v
Docker version 26.0.0, build 2ae903e

$ sudo systemctl start docker
$ sudo systemctl enable docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
```

# Git インストール & クローン
Git コマンドをインストールし、

```shell:
sudo dnf install -y git
```

上記 GitHub からレポジトリをクローンします。

```shell:
git clone https://github.com/kyouheicf/cloudflare-guacamole.git \
&& cd $(basename $_ .git)
```

# Access Application 作成
以下の手順に従って、 `Self-Hosted` の Access Application を作成します。

ここで使う公開ドメインは事前に Cloudflare に登録しておく必要があります。

https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-apps/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/4419a2ba-83f4-0bb1-de2a-2910ef127d2e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/d9c5cbe5-5935-213b-85df-7d21ef7db60a.png)

以下のポリシーでは `Default Deny` な形となりますが、事前に連携しておいた IdP で認証されたユーザに対してのみ、許可するルールを設定します。

https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/8592c9b4-abc2-3723-427a-38961bad2ef3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/8dd7338c-cf64-3834-a42a-6584a791d009.png)

# Cloudflare Tunnel 作成
Apache Guacamole 用に Cloudflare Tunnel を作成し、Debian 用のコマンドをメモしておきます。
トークンとして払い出される認証情報を後で使います。

https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/31cd01b4-8347-b93e-3907-aeea0cca6650.png)

作成した Tunnel 設定で、Public Hostname（`guacamole.yourdomain.com`）を紐付けます。

`guacamole.yourdomain.com` から入ってきた通信が `cloudflared` が稼働するコンテナの `http://127.0.0.1:8080/` にルーティングされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/c931f9c2-cfcd-e442-9d1e-47da01769557.png)


# `start.sh` 修正
先ほどクローンした GitHub レポジトリ `cloudflare-guacamole` フォルダ内の `start.sh` の最後の行を以下のように、Cloudflare Tunnel 作成時に提供されるトークン情報を入れて更新します。

```diff_shell:start.sh
- cloudflared tunnel run --token eyJhIjoiYj...
+ cloudflared tunnel run --token <YOUR_TUNNEL_TOKEN>
```

これで事前準備は完了です。

# Build & Run
以下のコマンドで `Dockerfile` からコンテナをビルドして稼働させます。

```shell:
sudo ./buildrun.sh 
```

```shell:
[+] Building 126.2s (34/34) FINISHED                                                                                                                                                                       docker:default
 => [internal] load build definition from Dockerfile                                                                                                                                                                 0.0s
 => => transferring dockerfile: 1.88kB                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                                                                                                                                     0.0s
 => [internal] load .dockerignore                                                                                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                                                                                      0.0s
 => CACHED [ 1/29] FROM docker.io/library/ubuntu:latest                                                                                                                                                              0.0s
 => [internal] load build context                                                                                                                                                                                    0.0s
 => => transferring context: 1.56kB                                                                                                                                                                                  0.0s
 => [ 2/29] ADD setup.sql /root/setup.sql                                                                                                                                                                            0.0s
 => [ 3/29] ADD start.sh /root/start.sh                                                                                                                                                                              0.0s
 => [ 4/29] RUN chmod +x /root/start.sh                                                                                                                                                                              0.4s
 => [ 5/29] RUN mkdir /etc/guacamole                                                                                                                                                                                 0.4s
 => [ 6/29] RUN mkdir /etc/guacamole/lib                                                                                                                                                                             0.4s
 => [ 7/29] ADD guacamole.properties /etc/guacamole/guacamole.properties                                                                                                                                             0.1s
 => [ 8/29] RUN apt update -y                                                                                                                                                                                        9.2s
 => [ 9/29] RUN apt install wget -y                                                                                                                                                                                  8.3s 
 => [10/29] RUN wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -O /root/cloudflared.deb                                                                         1.8s 
 => [11/29] RUN dpkg -i /root/cloudflared.deb                                                                                                                                                                        1.1s 
 => [12/29] RUN rm /root/cloudflared.deb                                                                                                                                                                             0.4s 
 => [13/29] RUN apt install guacd -y                                                                                                                                                                                42.4s 
 => [14/29] RUN apt install tomcat9 -y                                                                                                                                                                              17.2s 
 => [15/29] RUN apt install iproute2 -y                                                                                                                                                                              5.3s 
 => [16/29] RUN apt install mariadb-server -y                                                                                                                                                                       16.3s 
 => [17/29] RUN apt install libmariadb-java -y                                                                                                                                                                       4.0s 
 => [18/29] RUN mkdir /var/run/mysqld                                                                                                                                                                                0.4s 
 => [19/29] RUN chown -R mysql:root /var/run/mysqld                                                                                                                                                                  0.4s 
 => [20/29] RUN wget "https://archive.apache.org/dist/guacamole/1.3.0/binary/guacamole-1.3.0.war" -O /var/lib/tomcat9/webapps/guacamole.war                                                                          4.2s 
 => [21/29] RUN ln -s /etc/guacamole/ /var/lib/tomcat9/.guacamole                                                                                                                                                    0.4s 
 => [22/29] RUN mkdir /usr/share/tomcat9/logs                                                                                                                                                                        0.4s 
 => [23/29] RUN ln -s /usr/share/java/mariadb-java-client.jar /etc/guacamole/lib/                                                                                                                                    0.4s 
 => [24/29] RUN export CATALINA_HOME=/usr/share/tomcat9                                                                                                                                                              0.4s 
 => [25/29] RUN export CATALINA_BASE=/var/lib/tomcat9                                                                                                                                                                0.5s 
 => [26/29] RUN mkdir /etc/guacamole/extensions                                                                                                                                                                      0.4s 
 => [27/29] RUN wget https://archive.apache.org/dist/guacamole/1.3.0/binary/guacamole-auth-jdbc-1.3.0.tar.gz -O /root/guacamole-auth-jdbc-1.3.0.tar.gz                                                               4.4s
 => [28/29] RUN tar xvfz /root/guacamole-auth-jdbc-1.3.0.tar.gz -C /root/                                                                                                                                            0.7s 
 => [29/29] RUN cp /root/guacamole-auth-jdbc-1.3.0/mysql/guacamole-auth-jdbc-mysql-1.3.0.jar /etc/guacamole/extensions                                                                                               0.9s 
 => exporting to image                                                                                                                                                                                               4.8s 
 => => exporting layers                                                                                                                                                                                              4.7s 
 => => writing image sha256:92383533a2e54e8c4627c370aa7b03874837fcaa82f62363641b28687efeea5e                                                                                                                         0.0s 
 => => naming to docker.io/library/cloudflareguacamole                                                                                                                                                               0.0s 
bd8f9f59b3bdb3a069f2f9052652d306b31ef29be3bb653df14cad451c1f6d0e       
```

起動ログ

```
sudo docker logs --tail 50 cloudflareguacamole
```

```shell:
2024-04-09  4:51:17 0 [Note] Starting MariaDB 10.6.16-MariaDB-0ubuntu0.22.04.1 source revision  as process 7
2024-04-09  4:51:17 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2024-04-09  4:51:17 0 [Note] InnoDB: Using transactional memory
2024-04-09  4:51:17 0 [Note] InnoDB: Number of pools: 1
2024-04-09  4:51:17 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
2024-04-09  4:51:17 0 [Note] mariadbd: O_TMPFILE is not supported on /tmp (disabling future attempts)
2024-04-09  4:51:17 0 [Warning] mariadbd: io_uring_queue_init() failed with errno 1
2024-04-09  4:51:17 0 [Warning] InnoDB: liburing disabled: falling back to innodb_use_native_aio=OFF
2024-04-09  4:51:17 0 [Note] InnoDB: Initializing buffer pool, total size = 134217728, chunk size = 134217728
2024-04-09  4:51:17 0 [Note] InnoDB: Completed initialization of buffer pool
2024-04-09  4:51:17 0 [Note] InnoDB: 128 rollback segments are active.
2024-04-09  4:51:17 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2024-04-09  4:51:17 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2024-04-09  4:51:17 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
2024-04-09  4:51:17 0 [Note] InnoDB: 10.6.16 started; log sequence number 42514; transaction id 14
2024-04-09  4:51:17 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
2024-04-09  4:51:17 0 [Note] Plugin 'FEEDBACK' is disabled.
2024-04-09  4:51:17 0 [Warning] You need to use --log-bin to make --expire-logs-days or --binlog-expire-logs-seconds work.
2024-04-09  4:51:17 0 [Note] Server socket created on IP: '127.0.0.1'.
2024-04-09  4:51:17 0 [Note] InnoDB: Buffer pool(s) load completed at 240409  4:51:17
2024-04-09  4:51:17 0 [Note] mariadbd: ready for connections.
Version: '10.6.16-MariaDB-0ubuntu0.22.04.1'  socket: '/run/mysqld/mysqld.sock'  port: 3306  Ubuntu 22.04
guacd[26]: INFO:	Guacamole proxy daemon (guacd) version 1.3.0 started
Tomcat started.
2024-04-09T04:51:23Z INF Starting tunnel tunnelID=5359ca6a-c38b-4062-81c3-1769a70e6503
2024-04-09T04:51:23Z INF Version 2024.3.0
2024-04-09T04:51:23Z INF GOOS: linux, GOVersion: go1.21.5, GoArch: amd64
2024-04-09T04:51:23Z INF Settings: map[token:*****]
2024-04-09T04:51:23Z INF Generated Connector ID: 49352317-25a8-4591-8942-4d039e49d4cd
2024-04-09T04:51:23Z INF cloudflared will not automatically update if installed by a package manager.
2024-04-09T04:51:23Z INF Initial protocol quic
2024-04-09T04:51:23Z INF ICMP proxy will use 172.17.0.2 as source for IPv4
2024-04-09T04:51:23Z INF ICMP proxy will use fe80::42:acff:fe11:2 in zone eth0 as source for IPv6
2024-04-09T04:51:23Z INF Starting metrics server on 127.0.0.1:33147/metrics
2024/04/09 04:51:23 failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.
2024-04-09T04:51:23Z INF Registered tunnel connection connIndex=0 connection=326ac753-9a7f-42a9-bff8-24850edfd424 event=0 ip=198.41.192.167 location=nrt01 protocol=quic
2024-04-09T04:51:24Z INF Registered tunnel connection connIndex=1 connection=a6127fe5-8a96-4e94-a41d-14ebead26be3 event=0 ip=198.41.200.113 location=nrt07 protocol=quic
2024-04-09T04:51:24Z INF Updated to new configuration config="{\"ingress\":[{\"hostname\":\"guacamole.yourdomain.com\",\"originRequest\":{\"access\":{\"audTag\":[],\"required\":false,\"teamName\":\"xxxxxxx\"}},\"service\":\"http://127.0.0.1:8080\"},{\"service\":\"http_status:404\"}],\"warp-routing\":{\"enabled\":false}}" version=1
2024-04-09T04:51:25Z INF Registered tunnel connection connIndex=2 connection=931127c2-ebc7-4097-995a-07e71d0d3114 event=0 ip=198.41.192.27 location=nrt05 protocol=quic
2024-04-09T04:51:26Z INF Registered tunnel connection connIndex=3 connection=c2946800-d5b0-4f86-a236-c38a18848ebc event=0 ip=198.41.200.13 location=nrt08 protocol=quic
```

# Apache Guacamole にアクセス

`https://guacamole.yourdomain.com/guacamole/` にアクセスします。

まず Cloudflare Access で保護された認証画面が表示されるので、認証を済ませます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/d84ceb6c-8097-b657-c160-8ca3c14d3f5a.png)

その後、初期ユーザ名 `guacadmin` 、パスワード `guacadmin` でログインできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3d3cd476-3a3e-5e69-67ad-d5d91deed769.png)

Apache Guacamole の設定で、表示言語を日本語にして、パスワード変更もしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/a9c6ce13-b070-ae64-6128-930a280becf6.png)

# RDP 接続先の追加
以下のように設定し、「サーバ証明書を無視する」にチェックします。

https://www.designet.co.jp/ossinfo/apacheguacamole/guacamole_adminmanual/rdp_connection.php

>サーバ証明書を無視する
チェックを入れた場合、証明書を検証できない場合でも、サーバから返された証明書は無視されます。自己署名証明書を利用している場合など、サーバの証明書を検証できないことがわかっている場合に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/99e2862a-0d0c-d0f5-9fc8-8f258d32fd3f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/1c379387-a434-032f-8e05-fd3e36a85eae.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/a3a77b07-4440-aee8-2594-d08a497d7f08.png)

わかりやすいように「壁紙の有効化」もチェックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/8df85b00-0b11-75d2-8390-87bd6f4fd07b.png)

# ブラウザだけで RDP 接続できることを確認
Apache Guacamole ホーム画面に戻ると、登録された RDP 接続先が表示されますので、クリックして接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/a366b39c-3577-272f-f6e8-8bea3bb3f70c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/e278c3e8-152b-fde8-bd3a-91724d45ccf4.png)

これで無事ブラウザだけで RDP 接続できることが確認できました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/daacef0c-094f-6c2d-9da2-aefef7e87613.png)

ちなみにサインアウトすると、以下のような画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3d28a01a-ee8d-5f2a-9171-302f3865eb7e.png)

また、こうした RDP 接続に対して履歴で確認することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/c2ead97c-392b-32c5-e640-ee13241963f5.png)

# まとめ

Apache Guacamole では他にもスクリーンレコーディングやユーザ/グループに対する接続権限管理など、リモートアクセスのゲートウェイとして活用できる機能があります。

個人用途にも十分使えそうで、Cloudflare と組み合わせることでも簡単にセキュアなセットアップができることがわかりました。

https://www.designet.co.jp/ossinfo/apacheguacamole/

# 参考
- [Install Apache Guacamole as Docker Container on Ubuntu - kifarunix.com](https://kifarunix.com/install-apache-guacamole-as-docker-container-on-ubuntu/)
- [Kubernetes · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/deployment-guides/kubernetes/)