Repository navigation
Releases
v0.4.0
Running sudo vps-ready with no arguments now starts a guided setup: it looks at the server, recommends a profile, asks a few plain questions and applies the result behind the same dead-man switch as apply. This release also adds vps-ready modules and fixes the issues found in a security review of v0.3.0 (none critical or high).
What's new
- Guided setup:
sudo vps-readyon a terminal recommends a profile (minimalorweb), asks what the server runs, which admin user to use and which ports to open, shows the plan, and applies it with the safety timer. It uses the same code asapply --profile, so the plan, the confirm step and the rollback are the same. In a script or a pipe (stdin or stdout not a terminal) the bare command prints help and exits 2, as before. - Saving profiles: the wizard ends with the command that repeats the setup without questions. When the setup needs more than a built-in profile, it offers to save the profile to
vps-ready.tomlin the current directory and printsapply --profilewith its full path. vps-ready modules [ID]: lists the modules and whether each applies to this server. With an ID, it shows why the module matters, its parameters and the audit controls it fixes. No root needed;--jsonis available.
Security fixes
- Firewall (medium):
firewall.inboundtreats a listener as sshd only when it runs/usr/sbin/sshdas root, so a local process namedsshdcan no longer get its port opened. authorized_keys(medium): the admin's key file is read without following links, and a file that is hard-linked, owned by another user or has setuid/setgid bits is refused.- Confirm (low):
confirmrefuses a login from the server itself (loopback), which says nothing about reaching it from outside. - Docker bridges (low): the firewall's
br-*rule no longer accepts bridges that Docker did not create. - Rollback: if restoring the SSH or firewall config fails during a rollback, the admin account and its keys are kept until that restore succeeds, instead of being removed while root and password logins are still off.
Notes
- On Ubuntu, when you run the wizard as root and an
admingroup already exists, the new admin is namedsysadmin. - New runtime dependency:
dialoguer, used only for the wizard's prompts. - Debian 12/13 and Ubuntu 22.04/24.04 with systemd, as in v0.3.0.
Upgrade
Re-run the installer from this release:
curl -fsSLO https://github.com/bhimrazy/vps-ready/releases/download/v0.4.0/install.sh
less install.sh
sh install.sh
With Rust installed, cargo install vps-ready --locked works too. The security fixes apply only after the upgrade.
Verify the download:
sha256sum --check --ignore-missing checksums.txt
cosign verify-blob --bundle checksums.txt.sigstore.json \
--certificate-identity-regexp '^https://github.com/bhimrazy/vps-ready/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com checksums.txt
gh attestation verify vps-ready-0.4.0-x86_64-unknown-linux-musl.tar.gz --repo bhimrazy/vps-ready
v0.3.0
vps-ready can now harden SSH and the firewall without locking you out: changes that could cut you off are armed with a dead-man switch before they are made, and they stay only if a new SSH login confirms them.
- Four modules:
user.admin(a login user other than root, with your keys and sudo),ssh.server(one drop-in,/etc/ssh/sshd_config.d/00-vps-ready.conf, that turns off root and password logins; fixes SSH-001..007),firewall.inbound(default-deny inbound in vps-ready's own nftables table, always allowing every current SSH port; fixes FW-001) andssh.bruteforce(fail2ban'ssshdjail on the journal, banning in nftables). - Dead-man switch:
applyarms a systemd timer and a boot hook before the first change. Unless confirmed, everything rolls back after 5 minutes (--deadman 1mto30m), after a crash or a dropped connection, and at the next boot. Files ofssh.serverandfirewall.inboundare restored even if edited during the window; the edit is kept asfiles/<n>.during. confirm: run the printedssh -t -o ControlPath=none -p 22 admin@<address> sudo vps-ready confirmfrom a new terminal. Only a new SSH login as the admin counts (tmux, screen, mosh and ControlMaster connections do not). No code to type.confirm --extendadds time.applywaits with a countdown: exit 0 confirmed, 1 rolled back, and new exit code 4 when you stop waiting with Ctrl-C (the timer keeps running).- Profiles:
--profile minimal(admin, SSH, firewall for SSH only, kernel settings, automatic updates) and--profile web(adds 80/443 andssh.bruteforce), or your own file withextends. A plainapplywithout--profileor--modulestill changes only kernel settings and automatic updates. - Audit: lists only failures and unknowns, with passes folded into the summary (
--allshows everything) and the fix command for each. - SSH-008 (low): repeated failed logins are slowed down, by OpenSSH's
PerSourcePenalties(9.8+) or fail2ban. 29 controls in total. historyshowspending-confirm (reverts in 3m12s);detectshows your SSH session from the process tree.- New library crate
sshd-configon crates.io: readssshd_configwith itsIncludes and parsessshd -T.
Upgrade notes
- Do not downgrade to roll back. A v0.2 binary cannot read v0.3 journals (
armed,reloaded,confirmedand the other new events). Roll back v0.3 transactions with v0.3. - v0.2 journals still read and roll back. A journal without the uid of an account it created falls back to the name when deciding whether
userdelis safe. - Rolling back
ssh.bruteforceafter it installed fail2ban removes the jail but leavesfail2baninstalled and enabled (the package enables it before vps-ready records its state); the plan says so. Packages are never removed. - The dead-man modules need the binary in a root-owned path that only root can write, such as
/usr/local/bin(the installer's default), because a root timer runs it.
Known limits
- Debian 12/13 and Ubuntu 22.04/24.04 with systemd only; the dead-man modules need systemd.
- The printed
sshline shows the private address on NATed hosts (cloud floating IPs); use the address you connected to. firewall.inboundis not applied while ufw or firewalld is active, or with iptables-legacy rules loaded. It filters input only: Docker's published ports bypass it (FW-002 is reported, not fixed), and other input chains still apply.Matchblocks for other users or addresses are not checked; verify covers the admin's own connection.- A login through a jump host can confirm; mosh, tmux, screen and the console cannot.
userdelfails while the admin still has processes (exit 3, with the by-hand step); the user is restored last, so access is already back. A created user's home directory stays.- The SSH port stays 22.
Note
Verified in KVM VMs on Debian 12/13 and Ubuntu 22.04/24.04 (the vm workflow): --profile minimal with a new admin, where root and password logins are refused during the window, a confirm from a new login as the admin, SSH-001..007 and FW-001 passing, and rollback --last restoring root login; an unconfirmed apply reverted at the deadline, with an edit made during the window kept as .during; a reboot during the window rolled back before sshd started; confirm refused from the applying session, an older session and a ControlMaster connection. By hand in Lima (Debian 13, Ubuntu 24.04): port 80 dropped while 22 stayed open from another network namespace, and a DHCP renewal under the firewall. ssh.bruteforce and user.admin run end to end in containers on all four distros in CI.
Verify the download:
sha256sum --check --ignore-missing checksums.txt
cosign verify-blob --bundle checksums.txt.sigstore.json \
--certificate-identity-regexp '^https://github.com/bhimrazy/vps-ready/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com checksums.txt
gh attestation verify vps-ready-0.3.0-x86_64-unknown-linux-musl.tar.gz --repo bhimrazy/vps-ready
What's Changed
- ci: layer 3 tests in KVM virtual machines by @bhimrazy in #40
- ci: pin runners to ubuntu-24.04 by @bhimrazy in #42
- chore(deps): bump the github-actions group across 1 directory with 5 updates by @dependabot[bot] in #43
- ci: date the rust-toolchain pin comments by @bhimrazy in #44
- docs: show a real plan and apply session in the README by @bhimrazy in #45
- docs: plan M3 (dangerous modules and dead-man switch) by @bhimrazy in #46
- feat(cli): list only problems in audit, fold passes into the summary by @bhimrazy in #47
- refactor: sshd-config crate by @bhimrazy in #48
- feat(facts): SSH session from the process tree by @bhimrazy in #49
- feat(engine): reloads, module verify and params by @bhimrazy in #50
- feat(config): minimal and web profiles by @bhimrazy in #52
- feat(resource): Service, User and AuthorizedKeys by @bhimrazy in #51
- fix(sys): never follow a symlinked parent when RealFs changes a file by @bhimrazy in #53
- feat(modules): ssh.bruteforce by @bhimrazy in #55
- feat(engine): dead-man states, arming and rollback entry points by @bhimrazy in #54
- feat(cli): confirm and the pending countdown by @bhimrazy in #59
- feat(modules): user.admin by @bhimrazy in #56
- feat(modules): firewall.inbound by @bhimrazy in #57
- feat(modules): ssh.server by @bhimrazy in #58
- chore: release v0.3.0 by @bhimrazy in #61
New Contributors
- @dependabot[bot] made their first contribution in #43
Full Changelog: v0.2.1...v0.3.0
v0.2.1
A fix for rolling back kernel settings after a reboot, found by running v0.2.0 on a real Ubuntu 24.04 VM, plus clearer output.
rollbackafter a reboot no longer refuses, and never suggests weakening a setting. Kernel settings the boot already reset are left to the boot configuration and listed; vps-ready's own files are restored as before. Within the same boot rollback is as strict as before, and v0.2.0 journals keep the strict rule.auditends with how many failures have an automatic fix; a failure with none saysfix: not automated yet.plansummarizes by kind ("2 files, 6 kernel settings") and shows owners asroot:root;apply -n/--dry-runis an alias forplan.vps-readywith no command shows a short first-run guide; notices print as plainvps-ready: ...lines; the success line names the journal; the installer's signature note is one calm line.
Note
Verified on a real Ubuntu 24.04 VM: apply, reboot, rollback, reboot returns every kernel setting to the distro default. A kernel setting lowered at runtime below the kernel's built-in default (Ubuntu's perf_event_paranoid=4) is persisted at the module's value, which passes its control; rollback removes it.
What's Changed
- feat(cli): clearer audit, plan and first-run output by @bhimrazy in #38
- fix(engine): leave kernel settings to the boot config when rolling back after a reboot by @bhimrazy in #39
- chore: release v0.2.1 by @bhimrazy in #41
Full Changelog: v0.2.0...v0.2.1
v0.2.0
vps-ready can now change a host, not only audit it: plan shows every change with a diff, apply makes it with a crash-safe transaction journal, and rollback --last undoes it.
- New commands:
plan,apply,rollbackandhistory.apply,rollbackandhistoryneed root. - Two modules:
kernel.sysctl(fixes KRN-001..011 and NET-001..003) andupdates.auto(installsunattended-upgrades, fixes UPD-001).auditnow prints afix:line under each control a module can fix. - New controls NET-004..006 (IPv4 reverse path filtering and ICMP redirects), for 28 in total. FW-001 now works on iptables-legacy hosts.
- Exit codes: 0 ok, 1 apply failed and rolled back cleanly, 2 error, 3 rollback did not complete.
Note
kernel.sysctl is covered by unit, property and rollback tests on a simulated /proc/sys; it has not yet been run on a real VM, which containers cannot do. Try it on a throwaway server first and report issues. updates.auto is tested end to end in Debian and Ubuntu containers in CI.
What's Changed
- fix(audit): FW-001 on iptables-legacy hosts and the sudo hint without nft by @bhimrazy in #24
- docs: plan M2 (engine and safe modules) by @bhimrazy in #25
- feat(audit): check reverse path filtering and ICMP redirects per interface by @bhimrazy in #26
- feat(sys): writes through Fs by @bhimrazy in #27
- feat(resource): File and Sysctl resources by @bhimrazy in #28
- feat(engine): state directory, lock and journal by @bhimrazy in #29
- feat(resource): Package and file validation by @bhimrazy in #30
- feat(engine): apply, rollback and history by @bhimrazy in #31
- feat(modules): kernel.sysctl by @bhimrazy in #33
- feat(modules): updates.auto by @bhimrazy in #32
- feat(cli): plan, apply, rollback and history by @bhimrazy in #34
- test(containers): apply and roll back updates.auto by @bhimrazy in #35
- feat(cli): show progress during apply and rollback by @bhimrazy in #37
- chore: release v0.2.0 by @bhimrazy in #36
Full Changelog: v0.1.0...v0.2.0
v0.1.0
v0.1.0-alpha.5
v0.1.0-alpha.4
What's Changed
- fix: handle kernel settings that only root can read by @bhimrazy in #10
- docs: show the 18-control audit in the README demo by @bhimrazy in #11
- ci: run detect and audit in systemd containers by @bhimrazy in #12
- feat(audit): check that security updates install automatically by @bhimrazy in #13
- feat(audit): check that the firewall denies incoming traffic by default by @bhimrazy in #14
- chore: release v0.1.0-alpha.4 by @bhimrazy in #16
- ci: let the release tag push be the approval by @bhimrazy in #17
Full Changelog: v0.1.0-alpha.3...v0.1.0-alpha.4
v0.1.0-alpha.3
What's Changed
- feat(audit): add kernel and network sysctl controls by @bhimrazy in #8
- chore: release v0.1.0-alpha.3 by @bhimrazy in #9
Full Changelog: v0.1.0-alpha.2...v0.1.0-alpha.3
v0.1.0-alpha.2
What's Changed
- feat(facts): detect virtualization, SSH, listeners and session by @bhimrazy in #1
- feat(audit): add controls catalog and audit command by @bhimrazy in #2
- chore: dual-license under MIT OR Apache-2.0 by @bhimrazy in #3
- refactor: rename to vps-ready and add the vpsready alias crate by @bhimrazy in #4
- docs(release): record trusted publishing only on both crates by @bhimrazy in #5
- chore: release v0.1.0-alpha.2 by @bhimrazy in #6
- docs: refresh the README and prepare the docs for a public repo by @bhimrazy in #7
New Contributors
Full Changelog: https://github.com/bhimrazy/vps-ready/commits/v0.1.0-alpha.2