Fly.io でスマホから SSH 接続でき、アイドル時には自動停止するターミナル環境を立てる

linux

Fly.io は Lambda MicroVMs と同じくアプリケーションを動かしたりシェルに入ることができる VM を Firecracker で高速に立ち上げられる PaaS。最近 CEO が Docker の元 CEO に変わった

Fly Proxy がリクエストを受け、停止している VM を立ち上げ WireGuard トンネルを通してくれるので suspend 状態から純粋な SSH コマンドで接続することができる。これにより最小限の維持コストで Termius などのスマホアプリから接続することが可能になっている。

Lambda MicroVMs で capabilities 付きのサンドボックス環境を立ち上げシェルに入り docker run する - sambaiz-net

flyctl をインストールしてログインする。

$ brew install flyctl
$ fly auth login

アプリとボリュームを作成し、公開鍵を Secrets に保存する。

$ fly apps create fly-ssh-box
$ fly volumes create ssh_data --region nrt --size 1 -a fly-ssh-box
$ fly secrets set "AUTHORIZED_KEYS=ssh-ed25519 *****" -a fly-ssh-box

sshd をインストールし Secrets から authorized_keys を読んで配置するスクリプトを実行する Dockerfile と 停止時の挙動やマウントするボリュームなどの設定を書いた fly.toml を用意する。

$ tree .
.
├── Dockerfile
├── entrypoint.sh
└── fly.toml

$ cat Dockerfile
FROM ubuntu:24.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends openssh-server \
 && rm -rf /var/lib/apt/lists/* \
 && sed -i 's/^#\?Port.*/Port 2222/' /etc/ssh/sshd_config \
 && sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config \
 && sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config \
 && mkdir -p /root/.ssh /var/run/sshd \
 && chmod 700 /root/.ssh \
 && chmod 755 /var/run/sshd

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 2222
ENTRYPOINT ["/entrypoint.sh"]
CMD ["sleep", "infinity"]

$ cat entrypoint.sh
#!/bin/bash -e

# Persist host keys across deploys
mkdir -p /data/ssh_host_keys /root/.ssh
if [ -z "$(ls -A /data/ssh_host_keys 2>/dev/null)" ]; then
  cp /etc/ssh/ssh_host_*_key* /data/ssh_host_keys/
else
  cp /data/ssh_host_keys/ssh_host_*_key* /etc/ssh/
fi

if [ -n "$AUTHORIZED_KEYS" ]; then
  echo "$AUTHORIZED_KEYS" > /root/.ssh/authorized_keys
  chmod 600 /root/.ssh/authorized_keys
fi

/usr/sbin/sshd

exec "$@"

$ cat fly.toml
app = "fly-ssh-box"
primary_region = "nrt"

[build]

[mounts]
  source = "ssh_data"
  destination = "/data"

[[services]]
  internal_port = 2222
  protocol = "tcp"
  auto_stop_machines = "suspend"
  auto_start_machines = true
  min_machines_running = 0

  [[services.ports]]
    port = 22

デプロイし IP アドレスを割り当てて SSH 接続できることを確認する。スマホからも接続できた。なお IPv4 の割り当ては Trial では行えず $2/月 かかる。

$ fly deploy
==> Verifying app config
Validating /home/sambaiz/fly-ssh-box/fly.toml
✓ Configuration is valid
--> Verified app config
==> Building image
==> Building image with Depot
--> build:
[+] Building 18.1s (9/9) FINISHED
 => [internal] load build definition from Dockerfile
...

$ fly ips allocate-v4 -a fly-ssh-box
$ ssh [email protected]
# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.4 LTS"
...