This repository has no description
7.1 kB
179 lines
1#!/usr/bin/env bash
2# bootstrap.sh — provision a fresh ubuntu-24.04 box as a tangled.org knot + spindle host.
3# rendered by terraform's templatefile() with values substituted at apply time.
4# idempotent-ish: re-runs are safe but not the primary path. for re-provisioning use `just redeploy`.
5
6set -euo pipefail
7
8OWNER_DID="${owner_did}"
9KNOT_HOSTNAME="${knot_hostname}"
10SPINDLE_HOSTNAME="${spindle_hostname}"
11TANGLED_CORE_TAG="${tangled_core_tag}"
12ADMIN_EMAIL="${admin_email}"
13
14GO_VERSION="1.23.4"
15SOURCE_DIR="/opt/tangled-source"
16
17echo ">>> bootstrap starting at $(date -Iseconds)"
18echo ">>> owner DID: $${OWNER_DID}"
19echo ">>> knot hostname: $${KNOT_HOSTNAME}"
20echo ">>> spindle hostname: $${SPINDLE_HOSTNAME}"
21echo ">>> tangled core tag: $${TANGLED_CORE_TAG}"
22
23# ---------------------------------------------------------------------------
24# docker (required by spindle to spawn step containers)
25# ---------------------------------------------------------------------------
26echo ">>> installing docker"
27install -m 0755 -d /etc/apt/keyrings
28# --batch --yes makes gpg overwrite existing keyring silently (so re-runs work).
29curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
30 | gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
31chmod a+r /etc/apt/keyrings/docker.gpg
32
33. /etc/os-release
34echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $${VERSION_CODENAME} stable" \
35 > /etc/apt/sources.list.d/docker.list
36
37apt-get update
38apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
39systemctl enable --now docker
40
41# ---------------------------------------------------------------------------
42# go toolchain (to build knot + spindle from source)
43# ---------------------------------------------------------------------------
44echo ">>> installing go $${GO_VERSION}"
45ARCH=$(dpkg --print-architecture)
46case "$${ARCH}" in
47 amd64) GO_ARCH="amd64" ;;
48 arm64) GO_ARCH="arm64" ;;
49 *) echo "unsupported arch: $${ARCH}" >&2; exit 1 ;;
50esac
51curl -fsSL "https://go.dev/dl/go$${GO_VERSION}.linux-$${GO_ARCH}.tar.gz" \
52 | tar -C /usr/local -xz
53ln -sf /usr/local/go/bin/go /usr/local/bin/go
54ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
55
56# ---------------------------------------------------------------------------
57# build knot + spindle from @tangled.org/core
58# ---------------------------------------------------------------------------
59echo ">>> cloning tangled core @ $${TANGLED_CORE_TAG}"
60mkdir -p /opt
61# canonical clone URL — verified working via direct probe.
62# retry with backoff to ride out transient knot/appview hiccups.
63CLONE_URL="https://tangled.org/tangled.org/core"
64clone_with_retry() {
65 local attempt
66 for attempt in 1 2 3 4 5; do
67 if [ ! -d "$${SOURCE_DIR}/.git" ]; then
68 if git clone --depth 1 --branch "$${TANGLED_CORE_TAG}" "$${CLONE_URL}" "$${SOURCE_DIR}"; then
69 return 0
70 fi
71 rm -rf "$${SOURCE_DIR}"
72 else
73 if git -C "$${SOURCE_DIR}" fetch --tags && git -C "$${SOURCE_DIR}" checkout "$${TANGLED_CORE_TAG}"; then
74 return 0
75 fi
76 fi
77 echo ">>> clone attempt $${attempt} failed, sleeping $((attempt * 10))s..."
78 sleep $((attempt * 10))
79 done
80 return 1
81}
82clone_with_retry
83
84echo ">>> building knot"
85cd "$${SOURCE_DIR}"
86CGO_ENABLED=1 go build -o /usr/local/bin/knot ./cmd/knot
87chown root:root /usr/local/bin/knot
88chmod 0755 /usr/local/bin/knot
89
90echo ">>> building spindle"
91CGO_ENABLED=1 go build -o /usr/local/bin/spindle ./cmd/spindle
92chown root:root /usr/local/bin/spindle
93chmod 0755 /usr/local/bin/spindle
94
95# ---------------------------------------------------------------------------
96# knot: git user, env file, repo dir, sshd AuthorizedKeysCommand
97# ---------------------------------------------------------------------------
98echo ">>> setting up knot (git user, env, sshd)"
99if ! id -u git >/dev/null 2>&1; then
100 adduser --disabled-password --gecos "" git
101fi
102
103mkdir -p /home/git/repositories
104chown -R git:git /home/git
105
106cat > /home/git/.knot.env <<EOF
107KNOT_REPO_SCAN_PATH=/home/git/repositories
108KNOT_SERVER_HOSTNAME=$${KNOT_HOSTNAME}
109APPVIEW_ENDPOINT=https://tangled.org
110KNOT_SERVER_OWNER=$${OWNER_DID}
111KNOT_SERVER_INTERNAL_LISTEN_ADDR=127.0.0.1:5444
112KNOT_SERVER_LISTEN_ADDR=127.0.0.1:5555
113KNOT_SERVER_DB_PATH=/home/git/knotserver.db
114EOF
115chown git:git /home/git/.knot.env
116chmod 0640 /home/git/.knot.env
117
118# sshd: AuthorizedKeysCommand for git user — defers to knot for key lookup
119cat > /etc/ssh/sshd_config.d/knot.conf <<'EOF'
120Match User git
121 AuthorizedKeysCommand /usr/local/bin/knot keys -o authorized-keys -git-dir /home/git/repositories
122 AuthorizedKeysCommandUser nobody
123EOF
124systemctl reload ssh
125
126# install + start knot systemd unit
127install -m 0644 /opt/tangled-infra/knot.service /etc/systemd/system/knot.service
128systemctl daemon-reload
129systemctl enable --now knot.service
130
131# ---------------------------------------------------------------------------
132# spindle: spindle user (in docker group), env file, data + log dirs
133# ---------------------------------------------------------------------------
134echo ">>> setting up spindle (user, env, dirs)"
135if ! id -u spindle >/dev/null 2>&1; then
136 adduser --system --group --home /var/lib/spindle --shell /usr/sbin/nologin spindle
137fi
138usermod -aG docker spindle
139
140mkdir -p /var/lib/spindle /var/log/spindle /etc/spindle
141chown -R spindle:spindle /var/lib/spindle /var/log/spindle
142
143cat > /etc/spindle/env <<EOF
144SPINDLE_SERVER_HOSTNAME=$${SPINDLE_HOSTNAME}
145SPINDLE_SERVER_OWNER=$${OWNER_DID}
146SPINDLE_SERVER_LISTEN_ADDR=127.0.0.1:6555
147SPINDLE_SERVER_DB_PATH=/var/lib/spindle/spindle.db
148SPINDLE_PIPELINES_LOG_DIR=/var/log/spindle
149EOF
150chown spindle:spindle /etc/spindle/env
151chmod 0640 /etc/spindle/env
152
153install -m 0644 /opt/tangled-infra/spindle.service /etc/systemd/system/spindle.service
154systemctl daemon-reload
155systemctl enable --now spindle.service
156
157# ---------------------------------------------------------------------------
158# caddy: reverse proxy with letsencrypt for both hostnames
159# ---------------------------------------------------------------------------
160echo ">>> installing caddy"
161curl -fsSL "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" \
162 | gpg --batch --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
163curl -fsSL "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" \
164 > /etc/apt/sources.list.d/caddy-stable.list
165apt-get update
166apt-get install -y caddy
167
168install -m 0644 /opt/tangled-infra/Caddyfile /etc/caddy/Caddyfile
169systemctl enable --now caddy
170systemctl reload caddy
171
172# ---------------------------------------------------------------------------
173# done
174# ---------------------------------------------------------------------------
175echo ">>> bootstrap complete at $(date -Iseconds)"
176echo ">>> next steps:"
177echo " 1. point DNS A records for $${KNOT_HOSTNAME} and $${SPINDLE_HOSTNAME} at this server"
178echo " 2. verify the knot at https://tangled.org/settings/knots"
179echo " 3. verify the spindle at https://tangled.org/settings/spindles"