Single SSH session takes a fresh Arch box to a locked-down server running native OMATERM (28allday/omaterm@native-frozen, fetched read-only at runtime). - Phase A: hardening (deploy user, key-only SSH, ufw 22+tailscale0, fail2ban, weekly updates, swap, journal cap) — proven on tyler + durden 2026-06-06 - Phase B: hands off to install-native.sh as the deploy user with a real TTY; recovery net resumes first-run setup and lands in a shell if the chain exits early instead of dropping the SSH session - Every answer asked once: machine name = hostname + tailnet name + ssh alias; git identity pre-seeds ~/.gitconfig so omaterm setup skips it; answers survive the kernel-upgrade reboot - Kernel-upgrade guard offers the reboot in-script with exact resume command Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
2.8 KiB
Markdown
54 lines
2.8 KiB
Markdown
# omaserver
|
|
|
|
One-shot bootstrap for a fresh Arch Linux VPS: **hardened server + native [OMATERM](https://omaterm.org)**, in a single SSH session.
|
|
|
|
Part of the oma-family: [omalocal](https://github.com/28allday/omalocal) installs OMATERM on local hardware via a custom ISO — **omaserver** does the same job for a remote box you can only reach over SSH.
|
|
|
|
## Usage
|
|
|
|
Run as root on a fresh Arch VPS, **with a TTY** (the install ends in an interactive omaterm session):
|
|
|
|
```bash
|
|
ssh -t root@YOUR_SERVER 'bash <(curl -fsSL https://raw.githubusercontent.com/28allday/omaserver/main/install-server.sh)'
|
|
```
|
|
|
|
Answer four questions up front (machine name, deploy user, optional git identity) — each is asked **once** and reused everywhere:
|
|
|
|
| Answer | Used for |
|
|
|---|---|
|
|
| Machine name | hostname, Tailscale machine name, your local `ssh <name>` alias |
|
|
| Deploy user | the locked-down account that replaces root |
|
|
| Git identity | pre-seeds `~/.gitconfig` so omaterm's setup skips its git prompts |
|
|
|
|
If the first `pacman -Syu` upgrades the kernel, the script offers to reboot and your answers survive — reconnect, re-run the same command, and it picks up where it left off.
|
|
|
|
## What it does
|
|
|
|
**Phase A — hardening (as root)**
|
|
|
|
- Deploy user with key-only SSH (root's `authorized_keys` copied over), passwordless sudo
|
|
- Root SSH login disabled, password auth disabled, `AllowUsers <deploy-user>`
|
|
- UFW: deny incoming, allow 22/tcp + the `tailscale0` interface only
|
|
- fail2ban (systemd journal backend, ufw banaction)
|
|
- Weekly `pacman -Syu` timer, weekly package-cache pruning, 50MB journal cap
|
|
- 2GB swap (btrfs-aware; skipped if the image ships its own)
|
|
- Hostname, timezone, locale
|
|
|
|
**Phase B — OMATERM (as the deploy user)**
|
|
|
|
Hands off to the native OMATERM installer — packages, dotfiles, AI agents (claude, codex, gemini, opencode via mise), tmux workspace — then runs OMATERM's interactive first-run setup (GitHub, Tailscale, 1Password). The Tailscale hostname comes pre-filled with your machine name.
|
|
|
|
You end up inside a tmux session on a hardened box. If any part of the interactive setup bails early, the installer wrapper catches it, resumes setup, and lands you in a shell — the session is never just dropped.
|
|
|
|
To re-run or resume the OMATERM part later: `ssh -t <name> ./install-omaterm`
|
|
|
|
## Notes
|
|
|
|
- **Arch only** — refuses to run elsewhere.
|
|
- OMATERM is installed **natively** (no container) from the pinned fork [`28allday/omaterm@native-frozen`](https://github.com/28allday/omaterm/tree/native-frozen), which preserves the last upstream native install path. The fork is read-only at runtime; nothing is ever pushed to it.
|
|
- Want hardening without OMATERM? That's [arch-boot-strap](https://github.com/28allday/arch-boot-strap).
|
|
- Safe to re-run; completed steps skip themselves. After the first run root SSH login is disabled, so re-run as the deploy user with `sudo`.
|
|
|
|
## License
|
|
|
|
MIT
|