2ec6f73843
This commit combines multiple improvements for mobile support, reliability, and performance: Mobile & Accessibility Improvements: - Fix touch scrolling on mobile by removing CSS overflow-y:hidden that blocked xterm.js native scroll - Add beforeinput handler to prevent Android IME double-space-to-period duplication - Implement mobile control character toolbar (Esc, Tab, Ctrl/Alt toggles, arrows, special chars) - One-shot modifier toggles bring soft keyboard on demand without showing it for other keys - Replace 25+ unicode/HTML entity icons with inline SVGs across app for better rendering Security & Reliability: - Fix shell injection vulnerability: session names with spaces/special chars now properly quoted - Use shlex.quote() in create_session and delete_session endpoints - URL-encode session names in bell hook to prevent malformed curl URLs - Also hardens against command injection through crafted session names Performance Optimizations: - Eliminate 2.4-5.6 second session creation delay (was 3 compounding bottlenecks) - Frontend: check for new session immediately (was waiting 2s for interval tick) - Backend: eagerly refresh session cache after tmux creation (was waiting for next poll) - Connection: poll for ttyd readiness instead of blind 0.8s sleep - Typical new session now appears in UI in 200-400ms vs previous 4-5 seconds Infrastructure: - Improve ttyd port detection with multi-tool fallback (lsof → fuser → ss) - Add Cloudflare tunnel setup documentation Test Updates: - Update frontend tests to match new setTimeout recursion pattern (replaces setInterval) All 1306 tests pass. Generated with Amplifier Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
5.4 KiB
5.4 KiB
Cloudflare Tunnel Setup for Incus Host
Overview
Single routing point for all services across Incus instances. Services self-declare by dropping a file into a shared directory. No port forwarding, no OPNsense reverse proxy config, no DDNS.
Prerequisites
- Domain: ampbox.io (registrar: Namecheap)
- Cloudflare account (free tier) with ampbox.io DNS delegated
- At Namecheap: set nameservers to the ones Cloudflare assigns
- Verify domain is active in Cloudflare dashboard
1. Install cloudflared on the Incus host
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg \
| sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install -y cloudflared
2. Authenticate and create the tunnel
cloudflared tunnel login # opens browser, authorize ampbox.io
cloudflared tunnel create ampbox # creates tunnel, saves credentials JSON
cloudflared tunnel route dns ampbox "*.ampbox.io" # wildcard DNS route
Note the credentials file path printed (typically
~/.cloudflared/<TUNNEL_ID>.json). You need it for step 4.
3. Create the service declaration directory
sudo mkdir -p /etc/cloudflared/services.d
4. Write the main tunnel config
# Replace <TUNNEL_ID> with your actual tunnel ID from step 2
sudo tee /etc/cloudflared/config.yml << 'EOF'
tunnel: ampbox
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json
ingress:
- service: http_status:404
EOF
5. Share the declaration directory into all instances
incus profile device add default svc-declare disk \
source=/etc/cloudflared/services.d \
path=/mnt/services
6. Helper scripts (on the host)
/usr/local/bin/svc-rebuild
Reads all service declarations and rebuilds the cloudflared ingress config.
#!/usr/bin/env bash
set -euo pipefail
CONFIG="/etc/cloudflared/config.yml"
SERVICES_DIR="/etc/cloudflared/services.d"
TUNNEL_CREDS="/root/.cloudflared/<TUNNEL_ID>.json" # fix this
cat > "$CONFIG" <<HEADER
tunnel: ampbox
credentials-file: $TUNNEL_CREDS
ingress:
HEADER
for f in "$SERVICES_DIR"/*.yml; do
[ -f "$f" ] || continue
while IFS= read -r line; do
echo " $line" >> "$CONFIG"
done < "$f"
done
echo " - service: http_status:404" >> "$CONFIG"
sudo systemctl restart cloudflared
echo "Rebuilt with $(ls "$SERVICES_DIR"/*.yml 2>/dev/null | wc -l) service(s)"
sudo install -m 755 /dev/stdin /usr/local/bin/svc-rebuild < svc-rebuild.sh
/usr/local/bin/svc-watch
Auto-rebuilds on file changes (optional but recommended).
#!/usr/bin/env bash
set -euo pipefail
SERVICES_DIR="/etc/cloudflared/services.d"
echo "Watching $SERVICES_DIR for changes..."
while inotifywait -q -e create,delete,modify "$SERVICES_DIR"; do
sleep 1 # debounce
/usr/local/bin/svc-rebuild
done
sudo apt install -y inotify-tools
sudo install -m 755 /dev/stdin /usr/local/bin/svc-watch < svc-watch.sh
7. Systemd services (on the host)
/etc/systemd/system/cloudflared.service
[Unit]
Description=Cloudflare Tunnel
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/cloudflared tunnel run ampbox
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
/etc/systemd/system/svc-watch.service
[Unit]
Description=Service declaration watcher
After=cloudflared.service
[Service]
ExecStart=/usr/local/bin/svc-watch
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared svc-watch
8. Declaring services FROM INSIDE an instance
Expose an HTTP service
cat > /mnt/services/myapp.yml << EOF
- hostname: myapp.ampbox.io
service: http://$(hostname -I | awk '{print $1}'):8080
EOF
Expose SSH
cat > /mnt/services/ssh-$(hostname).yml << EOF
- hostname: ssh-$(hostname).ampbox.io
service: ssh://$(hostname -I | awk '{print $1}'):22
EOF
Remove a service
rm /mnt/services/myapp.yml
# svc-watch picks it up and rebuilds automatically
9. Client-side SSH config (on laptops / remote machines)
# ~/.ssh/config
Host *.ampbox.io
ProxyCommand cloudflared access ssh --hostname %h
Then: ssh user@ssh-myinstance.ampbox.io just works.
10. OPNsense cleanup
Once the tunnel is verified working, remove from OPNsense:
- All per-service reverse proxy rules in os-caddy
- Port forward / NAT rules for 80 and 443
- DDNS configuration (no longer needed)
OPNsense goes back to just being your firewall.
Quick Reference
| Action | From inside instance |
|---|---|
| Expose HTTP | echo "- hostname: X.ampbox.io\n service: http://IP:PORT" > /mnt/services/X.yml |
| Expose SSH | Same pattern with service: ssh://IP:22 |
| Expose raw TCP | Same pattern with service: tcp://IP:PORT |
| Remove service | rm /mnt/services/X.yml |
| List registered | ls /mnt/services/ |
| Check tunnel status | sudo systemctl status cloudflared (from host) |