自宅サーバのOpenHandsに外からアクセスできるようにした（メモ）

# 要約
- 自宅サーバにインストールしたOpenHandsにインターネットからアクセスできるようにした
- [この記事](https://qiita.com/uist1idrju3i/items/bd4e3eedf614bde7d329)の続きです

## 構成
```mermaid
flowchart LR
  wg1 <-- VPN --> wg2
  nginx <-- HTTPS --> cf(Cloudflare) <--> wan((The Internet))
  subgraph 自宅サーバ
    Docker <-- HTTP --> wg1[WireGuard]
  end
  subgraph VPS
    wg2[WireGuard] <-- HTTP --> nginx
  end
```

## 再起動時にDockerコンテナが自動起動するようにする(自宅サーバ)
- [前回の記事](https://qiita.com/uist1idrju3i/items/bd4e3eedf614bde7d329)をベースに、Dockerコンテナを立ち上げる際のオプションを変更しておく。
```console
# docker run --detach -it --restart unless-stopped --pull=always \
    -e SANDBOX_RUNTIME_CONTAINER_IMAGE=docker.all-hands.dev/all-hands-ai/runtime:0.27-nikolaik \
    -e LOG_ALL_EVENTS=true \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v ~/.openhands-state:/.openhands-state \
    -p 3000:3000 \
    --add-host host.docker.internal:host-gateway \
    --name openhands-app \
    docker.all-hands.dev/all-hands-ai/openhands:0.27
```

## VPNの構築(自宅サーバ, VPS)
- WireGuardをインストールする
```console
# apt install wireguard
```

- 鍵の生成(自宅サーバ, VPSの両方それぞれで作成する)
```console
# wg genkey > privatekey
# wg pubkey < privatekey > publickey
# wg genpsk > preshared
```

- 設定ファイルを作成する
```console
# chmod 700 /etc/wireguard
# touch /etc/wireguard/wg1.conf
# chmod 600 /etc/wireguard/wg1.conf
```

- VPS側の設定
```console
# vi /etc/wireguard/wg1.conf
	[Interface]
	PrivateKey = ＜VPSの秘密鍵＞
	Address = 172.17.0.1
	ListenPort = 1234
	[Peer]
	PublicKey = ＜自宅サーバの公開鍵＞
	PresharedKey = ＜自宅サーバ,VPSで同じ鍵を設定する＞
	AllowedIPs = 172.17.0.0/24
	PersistentKeepalive = 60

# ufw allow in on eth0 proto udp to ＜VPSのグローバルIP＞/32 port 1234
# systemctl enable wg-quick@wg1
# systemctl start wg-quick@wg1
# wg show
```

- 自宅サーバ側の設定
```console
# vi /etc/wireguard/wg1.conf
	[Interface]
	PrivateKey = ＜自宅サーバの秘密鍵＞
	Address = 172.17.0.2
	ListenPort = 1234
	[Peer]
	PublicKey = ＜VPSの公開鍵＞
	PresharedKey = ＜自宅サーバ,VPSで同じ鍵を設定する＞
	EndPoint = ＜VPSのグローバルIPアドレス＞:1234
	AllowedIPs = 172.17.0.0/24
	PersistentKeepalive = 60

# systemctl enable wg-quick@wg1
# systemctl start wg-quick@wg1
# wg show
```

## nginxインストール(VPS)
- nginxをインストールしてCloudflareの証明書を設定する
```console
# apt install nginx
# systemctl stop nginx
# rm /etc/nginx/sites-enabled/default
# mkdir /etc/nginx/cert
# chmod 700 /etc/nginx/cert
# openssl dhparam -out /etc/nginx/cert/dhparam.pem 4096
# vi /etc/nginx/cert/openhands.crt
# vi /etc/nginx/cert/openhands.key
# wget https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem
# rm /etc/nginx/modules-enabled/*
```

- このnginxの通信相手はCloudflareのみなのでTLS v1.3のみで良い
```console
# vi /etc/nginx/nginx.conf
    user www-data;
    worker_processes auto;
    worker_rlimit_nofile 204800;
    
    pid /run/nginx.pid;
    include /etc/nginx/modules-enabled/*.conf;
    
    events {
    	worker_connections 65535;
    	multi_accept on;
    	use epoll;
    }
    
    http {
    	##
    	# Basic Settings
    	##
    
    	sendfile on;
    	tcp_nopush on;
    	tcp_nodelay on;
    
    	keepalive_timeout 65;
    
    	types_hash_max_size 2048;
    	server_tokens off;
    
    	# server_names_hash_bucket_size 64;
    	# server_name_in_redirect off;
    
    	include /etc/nginx/mime.types;
    	default_type application/octet-stream;
    
    	##
    	# SSL Settings
    	##
    
    	ssl_protocols TLSv1.3;
    	ssl_prefer_server_ciphers on;
    
    	ssl_dhparam /etc/nginx/cert/dhparam.pem;
    
    	ssl_ecdh_curve          X25519:prime256v1;
    
    	ssl_session_timeout     1d;
    	ssl_session_cache       shared:SSL:128m;
    	ssl_session_tickets     off;
    
    	ssl_stapling            off;
    	ssl_stapling_verify     off;
    
    	resolver                127.0.0.53 valid=60s ipv6=off;
    	resolver_timeout        5s;
    
    	##
    	# Logging Settings
    	##
    
    	access_log /var/log/nginx/access.log;
    	error_log /var/log/nginx/error.log;
    
    	##
    	# Gzip Settings
    	##
    
    	gzip off;
    
    	# gzip_vary on;
    	# gzip_proxied any;
    	# gzip_comp_level 1;
    	# gzip_buffers 16 8k;
    	# gzip_http_version 1.1;
    	# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
    	##
    	# Virtual Host Configs
    	##
    
    	include /etc/nginx/conf.d/*.conf;
    	include /etc/nginx/sites-enabled/*;
    }
```

- OpenHandsはWebsocketを使用しているので忘れずにupgradeを書く
- OpenHandsには認証機能が無さそうなので、実験環境とはいえBASIC認証をかけておく
```console
# vi /etc/nginx/sites-available/openhands.conf
    map $http_upgrade $connection_upgrade {
            default upgrade;
            '' close;
    }
    
    server {
            listen 443 ssl http2 default_server;;
            listen [::]:443 ssl http2 default_server;;
    
            server_name example.com;
    
            root /var/www/public_html;
    
            ssl_certificate         /etc/nginx/cert/openhands.crt;
            ssl_certificate_key     /etc/nginx/cert/openhands.key;
            ssl_dhparam             /etc/nginx/cert/dhparam.pem;
    
            ssl_stapling            off;
            ssl_stapling_verify     off;

            ssl_client_certificate  /etc/nginx/cert/authenticated_origin_pull_ca.pem;
            ssl_verify_client       on;
    
            set_real_ip_from        0.0.0.0/0;
            set_real_ip_from        ::/0;
            real_ip_header          CF-Connecting-IP;
    
            keepalive_timeout       990;
            client_max_body_size    1m;
    
            location / {
                auth_basic "Restricted";
                auth_basic_user_file /etc/nginx/.htpasswd;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_set_header Authorization $http_authorization;
                proxy_pass    http://172.17.0.2:3000/;
                proxy_redirect off;
            }
    }
```
```console
# cd /etc/nginx/sites-enable
# ln -s /etc/nginx/sites-available/openhands.conf
```

- ファイアウォール設定
```console
# ufw allow in on eth0 proto tcp from 173.245.48.0/20 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 103.21.244.0/22 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 103.22.200.0/22 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 103.31.4.0/22 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 141.101.64.0/18 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 108.162.192.0/18 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 190.93.240.0/20 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 188.114.96.0/20 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 197.234.240.0/22 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 198.41.128.0/17 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 162.158.0.0/15 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 172.64.0.0/13 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 131.0.72.0/22 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 104.16.0.0/13 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 104.24.0.0/14 to ＜VPSのグローバルIPv4アドレス＞/32 port 443 comment 'Cloudflare'

# ufw allow in on eth0 proto tcp from 2400:cb00::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2606:4700::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2803:f800::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2405:b500::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2405:8100::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2a06:98c0::/29 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
# ufw allow in on eth0 proto tcp from 2c0f:f248::/32 to ＜VPSのグローバルIPv6アドレス＞/64 port 443 comment 'Cloudflare'
```

## 動作確認環境
- Ubuntu 24.04
- OpenHands 0.27.0 - 2025-02-27