Add root-ubuntu.sh: provision an Ubuntu/Debian server from one file - #24
Merged
Conversation
Moves the server provisioner out of the private dotfiles repo and into cli-tools, where it can be curled onto a box that has nothing on it yet. It is the odd one out here -- bash rather than TypeScript, and not linked onto PATH -- because it has to run before Node exists. Three things had to change for it to live in a public repo and work on a machine that is not one of ours. Dotfiles are now optional and are not in this repository. They cannot be: a dotfiles tree carries ssh config, known_hosts and sometimes keys. The script used to die unless it was sitting inside that checkout, which is exactly what stopped it running anywhere else. It now resolves a source in order -- DOTFILES_DIR, the directory holding the script, DOTFILES_REPO cloned to a cache, or nothing -- and skips only the dotfile stages when there is none. install_ssh had to be split for this: authorising the account's own key is not a dotfiles job, and an earlier cut of this returned early without one, so accounts got created that could not log in. Nothing identifying anybody is left in it. ACME_EMAIL has no default, because a made-up address sends a stranger's certificate warnings into a black hole, and certbot is passed --register-unsafely-without-email rather than an empty -m. There is no default ad slot: a slot id is an account, so shipping one bills every box that ever runs this to whoever owns it. Credentials come from the environment or the config file only. Configuration is read, not sourced. The environment has to win over the file, and `.` assigns unconditionally, so a sourced config would quietly beat the value someone just put on the command line -- and this runs as root, where sourcing hands a config file the whole machine. It is KEY=value, parsed, with nothing executed. Not JSON, because the script runs before apt has installed jq. Also: a POSIX guard that says so in one sentence when the script is run under sh, since /bin/sh on Ubuntu is dash and the old one-liner would have failed on the first [[ with a syntax error naming a line nobody typed; an OS check that warns rather than refuses; --groups, so accounts can be provisioned from a pipe; and the landing page still recognises the marker from when this lived in the dotfiles repo, without which every already-provisioned box would decide its own page was hand-edited and never touch it again. 34 tests cover the pure helpers by cutting them out of the file and running them in a real bash, plus the invariants that make it safe to publish: no personal identifiers, no key material, no default slot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ThreatCrush Security Scan11 finding(s) HIGH/CRITICAL: 5 | MEDIUM: 1 | LOW: 5
Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the server provisioner out of the private
dottemplatesrepo and intocli-tools, where it can be curled onto a box that has nothing on it yet. It is
the odd one out here — bash rather than TypeScript, and not linked onto
PATH— because it has to run before Node exists.
curl -fsSL https://raw.githubusercontent.com/profullstack/cli-tools/master/root-ubuntu.sh \ | bash -s -- --refreshThree things had to change for it to live in a public repo and work on a
machine that is not one of ours.
Dotfiles are optional and are not in this repository. They cannot be: a
dotfiles tree carries ssh config,
known_hostsand sometimes keys. The scriptused to die unless it was sitting inside that checkout, which is exactly what
stopped it running anywhere else. It now resolves a source in order —
DOTFILES_DIR, the directory holding the script,DOTFILES_REPOcloned to acache, or nothing — and skips only the dotfile stages when there is none.
install_sshhad to be split for this: authorising the account's own key is nota dotfiles job, and an earlier cut of this returned early without one, so
accounts got created that could not log in.
Nothing identifying anybody is left in it.
ACME_EMAILhas no default,because a made-up address sends a stranger's certificate warnings into a black
hole; certbot gets
--register-unsafely-without-emailrather than an empty-m. There is no default ad slot: a slot id is an account, so shipping onebills every box that ever runs this to whoever owns it.
Configuration is read, not sourced. The environment has to win over the
file, and
.assigns unconditionally, so a sourced config would quietly beatthe value someone just put on the command line — and this runs as root, where
sourcing hands a config file the whole machine. It is
KEY=value, parsed, withnothing executed. Not JSON, because the script runs before apt has installed
jq.Also in here:
sh,since
/bin/shon Ubuntu is dash and the one-liner would otherwise fail onthe first
[[with a syntax error naming a line nobody typed--groups, so accounts can be provisioned from a pipedotfiles repo, without which every already-provisioned box would decide its
own page was hand-edited and never touch it again
Testing
34 new tests (335 total,
tsc --noEmitclean). The pure helpers are cut out ofthe file with
sedand run in a real bash, so what is asserted is what the filedoes rather than what it appears to say. The rest guard the invariants that make
it safe to publish: no personal identifiers, no key material, no default slot,
and a config file whose
$(…)stays literal text.Not yet run as root on a real server — worth a throwaway VPS before a box
anybody depends on.
Two changed defaults
On an existing box, a re-run with no
/etc/cli-tools/server.confwill turn thesponsor ad off and issue certificates with no contact address. Both are
deliberate — neither value can ship in a public file — but they are live
behaviour changes.
SPONSOR_AD_SLOTandACME_EMAILin that config restorethem.
🤖 Generated with Claude Code