出先からも Taskwarrior を使うため ECS Fargate のコンテナに SSH 接続したい。
Taskwarrior と Timewarrior でコマンドラインからタスク管理とタイムトラッキングをする - sambaiz-net
iPhone (Termius)
↓ SSH
Fargate Task
└── Container (Debian)
├── tailscaled (userspace networking + SSH server)
└── EFS mount: /var/lib/tailscale
セキュアに繋ぐにあたって AWS であればポートを公開する必要がない SSM が選択肢に上がるが、スマホのような AWS CLI が動かせない環境だと使えないので Tailscale を使う。Tailscale は Coordination Server が接続情報を返した後は Linux カーネルにも組み込まれている VPN プロトコル WireGuard でエンドツーエンド暗号化して P2P 通信を行うサービス。パケットを送り合う UDP hole punching で NAT トラバーサルするので SSM と同じくインバウンドのポートを開く必要がない。
ssh-over-ssm で Session Manager を通して EC2 インスタンスに SSH 接続する - sambaiz-net
Tailscale をインストールする。
RUN curl -fsSL https://tailscale.com/install.sh | sh
Tailscale は通常 TUN (network TUNnel) デバイスでルーティングおよびハンドリングを行う。
$ ip tuntap add dev tun0 mode tun
$ ip addr add 10.200.0.1/24 dev tun0
$ ip link set tun0 up
$ ip route | grep tun0
# 10.200.0.0/24 dev tun0 proto kernel scope link src 10.200.0.1 linkdown
$ ip tuntap del dev tun0 mode tun
ただ Fargate のような TUN をサポートしていない環境ではこの方法が取れないので tailscaled を SOCKS5/HTTP プロキシとする userspace networking mode で動かす。
$ tailscaled --state=/var/lib/tailscale/tailscaled.state --tun=userspace-networking &
再起動後も同じデバイスとして認識させるため /var/lib/tailscale を EFS に永続化させる。
const fileSystem = new efs.FileSystem(this, 'Efs', { vpc });
const tailscaleAccessPoint = fileSystem.addAccessPoint('TailscaleAccessPoint', {
path: '/tailscale',
posixUser: { uid: '0', gid: '0' },
});
taskDef.addVolume({
name: 'tailscale-state',
efsVolumeConfiguration: {
fileSystemId: fileSystem.fileSystemId,
authorizationConfig: { accessPointId: tailscaleAccessPoint.accessPointId },
},
});
container.addMountPoints({
sourceVolume: 'tailscale-state',
containerPath: '/var/lib/tailscale',
readOnly: false,
});
authkey をコンソールで生成し tailscale up を実行すると keypair が生成され、公開鍵は Coordination Server に登録されて WireGuard での暗号化に使われる。キーはデフォルトで 180 日の有効期限があるので、サーバー用途では Disable key expiry を設定する。
$ tailscale up --authkey="${TS_AUTHKEY}" --ssh --reset
–ssh は Tailscale SSH を有効にするフラグ。tailscaled がポート 22 をインターセプトし認証を行う。ACL の設定が必要。
{
"ssh": [
{
"action": "accept",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["root"]
}
]
}
tailscale/gitops-acl-action を使うと push で ACL を反映できる。
on:
push:
paths:
- 'tailscale/policy.hujson'
jobs:
acl:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tailscale/gitops-acl-action@v1
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tailnet: ${{ secrets.TS_TAILNET }}
policy-file: tailscale/policy.hujson
action: apply
スマホに Tailscale アプリを入れてネットワークに接続すると Termius からデバイス名で SSH 接続できる。