From 778bef5ee6941056e06326d1eaaa6956d7307a85 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 18 Apr 2026 14:40:17 -0700 Subject: Remove Go implementation — ship is skills-only now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skills/ directory fully replaces the old Go CLI. Drop all Go source, build files, planning docs, and the stale SECURITY.md (which described the old git-user push-deploy model that no longer exists). Trim .gitignore to match the new tree. --- SECURITY.md | 55 ------------------------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 SECURITY.md (limited to 'SECURITY.md') diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 2d7a96e..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,55 +0,0 @@ -# Security Model & Known Gaps - -Ship is a single-user VPS deployment tool. The threat model assumes: -- You control the VPS and have root SSH access -- You trust everyone who has SSH push access (their keys are copied to the `git` user) -- The VPS runs only your own apps - -## Mitigations in place - -### App name validation -All app/project names are validated against `^[a-z][a-z0-9-]{0,62}$` before being used in shell commands, file paths, systemd units, or DNS labels. This prevents command injection via crafted names. - -### Scoped sudoers -The `git` user's sudo rules are restricted to specific paths: -- `systemctl restart/enable` only for services matching `[a-z]*` -- `cp` only from `.ship/` subdirectories to `/etc/systemd/system/` and `/etc/caddy/sites-enabled/` -- `mkdir` only under `/var/lib/` and `/var/www/` -- `chown` only for `git:git` under `/var/lib/` and `/var/www/` - -### Scoped safe.directory -Git's `safe.directory` is set only for the `www-data` user (not system-wide), preserving CVE-2022-24765 protection for other users. - -## Accepted risks (by design) - -### SSH key access = root access -The `git` user is in the `docker` group, which is root-equivalent (can mount the host filesystem). Additionally, `.ship/service` files pushed via git are installed as systemd units. Anyone with SSH push access effectively has root. This is intentional for a single-user tool. - -### Git repo visibility -Repos are private by default (not cloneable over HTTPS). Use `ship init --public` to make a repo publicly cloneable. This is controlled by the `git-daemon-export-ok` marker file in each bare repo. Only public repos are accessible via `go get` or `git clone` over HTTPS. The cgit web interface respects the same model — it is configured with `export-ok=git-daemon-export-ok`, so only public repos are browsable. - -### User-controlled systemd units -The `.ship/service` file in each repo is copied to `/etc/systemd/system/` on push. A malicious service file could run arbitrary commands as root. This is equivalent to the Docker access risk above. - -## Known gaps (not yet addressed) - -### SSH host key verification disabled -`ssh.InsecureIgnoreHostKey()` is used for all SSH connections, and `StrictHostKeyChecking=no` for scp/rsync. This makes connections vulnerable to MITM attacks on untrusted networks. A future improvement would use `known_hosts` verification. - -### Env files may have loose permissions -Environment files at `/etc/ship/env/{name}.env` are created via `sudo tee` and may be world-readable depending on umask. These files can contain secrets. The `deploy` flow does `chmod 600` but `ship init` does not. A future improvement would ensure consistent restrictive permissions. - -### host init is not idempotent -Running `ship host init` twice will overwrite `/etc/caddy/Caddyfile` and the base domain Caddy config, destroying any manual edits. No guard checks whether setup has already been completed. - -### No rollback on failed docker build -The post-receive hook installs `.ship/service` and `.ship/Caddyfile` before running `docker build`. If the build fails, the configs are updated but the old image is still running, creating a mismatch. The old container keeps running (due to `set -e`), but a manual restart would use the new (mismatched) unit file. - -### ship deploy vs git push ownership mismatch -`ship deploy` runs commands as root (the SSH user), while `git push` triggers the hook as the `git` user. Files checked out by `ship deploy` become root-owned, which can prevent subsequent `git push` deploys from overwriting them. - -### No concurrent push protection -Simultaneous pushes can race on the checkout directory and docker build. For single-user usage this is unlikely but not impossible. - -### Port allocation is monotonic -Ports are never reclaimed when apps are removed. After ~57,000 create/remove cycles, ports would be exhausted. Not a practical concern. -- cgit v1.2.3