-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·353 lines (296 loc) · 11 KB
/
install.sh
File metadata and controls
executable file
·353 lines (296 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/usr/bin/env bash
set -euo pipefail
# ==============================================================================
# Ubuntu Bun installer
# - Installs Nginx, UFW, Certbot (snap), Bun
# - Sets up a simple Bun app under /srv/app
# - Creates systemd service bun-app
# - Optionally configures UFW and writes application info + MOTD
#
# Environment toggles (set to 1 to skip):
# SKIP_BUN_APP=1 -> skip creating /srv/app Bun app
#
# Usage:
# sudo bash install.sh
# ==============================================================================
APP_DIR=/srv/app
NGINX_ROOT=/var/www/app/dist
BASE_PACKAGES=(curl unzip lsb-release ca-certificates nginx ufw snapd)
GREEN=$(printf '\033[0;32m')
YELLOW=$(printf '\033[1;33m')
RED=$(printf '\033[0;31m')
NC=$(printf '\033[0m')
TEMPLATE_BASE_URL="https://raw.githubusercontent.com/acfatah/ubuntu-bun-server-setup/refs/heads/main/templates"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATE_LOCAL_DIR="$SCRIPT_DIR/templates"
download_template() {
local template_name="$1"
local dest_path="$2"
mkdir -p "$(dirname "$dest_path")"
local local_template="$TEMPLATE_LOCAL_DIR/$template_name"
if [[ -f "$local_template" ]]; then
echo -e "${YELLOW}Using local template for $template_name...${NC}"
cp "$local_template" "$dest_path"
return
fi
echo -e "${YELLOW}Downloading template $template_name...${NC}"
curl -fsSL "${TEMPLATE_BASE_URL}/${template_name}" -o "$dest_path"
}
replace_placeholder() {
local target_file="$1"
local placeholder="$2"
local value="$3"
local escaped_placeholder
local escaped_value
escaped_placeholder=$(printf '%s' "$placeholder" | sed -e 's/[|&]/\\\&/g')
escaped_value=$(printf '%s' "$value" | sed -e 's/[|&]/\\\&/g')
sed -i "s|$escaped_placeholder|$escaped_value|g" "$target_file"
}
if [ -n "${SKIP_BUN_APP:-}" ]; then
NGINX_ROOT=/var/www/html
fi
# Uniquely identifies this installation instance
INSTANCE_ID=$(cat /proc/sys/kernel/random/uuid)
# Ensures the script is executed as root (UID 0).
require_root() {
if [[ $(id -u) -ne 0 ]]; then
echo -e "${RED}This installer must be run as root (use sudo).${NC}" >&2
exit 1
fi
}
# Verifies the host is Ubuntu by reading /etc/os-release.
require_ubuntu() {
if [[ -f /etc/os-release ]]; then
# shellcheck source=/etc/os-release
. /etc/os-release
if [[ ${ID:-} != "ubuntu" ]]; then
echo -e "${RED}This script targets Ubuntu. Detected: ${ID:-unknown}.${NC}" >&2
exit 1
fi
else
echo -e "${RED}/etc/os-release not found; cannot verify Ubuntu.${NC}" >&2
exit 1
fi
}
# Updates APT metadata and performs a non-interactive full upgrade.
# Uses --force-confdef/--force-confold to keep existing configs.
apt_update_upgrade() {
export DEBIAN_FRONTEND=noninteractive
apt-get -qqy update
# Keep existing configs if prompted
apt-get -qqy -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' full-upgrade
}
# Installs base packages needed by the stack.
install_base_packages() {
echo -e "${GREEN}Installing base packages: ${BASE_PACKAGES[*]}...${NC}"
apt-get -qqy install "${BASE_PACKAGES[@]}"
}
# Resets and configures UFW: deny incoming, allow outgoing, limit SSH, allow HTTP/HTTPS.
# Prefers the 'Nginx Full' app profile when available.
configure_ufw() {
echo -e "${GREEN}Configuring UFW (allow 'Nginx Full', limit ssh)...${NC}"
# Ensure profiles are loaded
if ufw --version >/dev/null 2>&1; then
ufw --force reset >/dev/null 2>&1 || true
ufw default deny incoming
ufw default allow outgoing
ufw limit ssh || true
# Allow Nginx HTTP+HTTPS profile if available, otherwise explicit ports
if ufw app list 2>/dev/null | grep -q "Nginx Full"; then
ufw allow 'Nginx Full' || true
else
ufw allow 80/tcp || true
ufw allow 443/tcp || true
fi
ufw --force enable
fi
}
# Installs Certbot via snap, removing any apt version to avoid conflicts.
install_certbot() {
echo -e "${GREEN}Installing Certbot (snap)...${NC}"
# Avoid older apt certbot
apt-get -qqy remove certbot || true
systemctl enable snapd >/dev/null 2>&1 || true
systemctl start snapd || true
# Wait for snapd socket
timeout 60 bash -c 'until snap list >/dev/null 2>&1; do sleep 2; done' || true
snap install --classic certbot || true
ln -sf /snap/bin/certbot /usr/bin/certbot
snap set certbot trust-plugin-with-root=ok || true
}
# Installs Bun if not present and ensures it's on PATH via symlink.
install_bun() {
if command -v bun >/dev/null 2>&1; then
echo -e "${YELLOW}Bun already installed: $(bun --version)${NC}"
return
fi
echo -e "${GREEN}Installing Bun...${NC}"
curl -fsSL https://bun.sh/install -o /tmp/bun_setup.sh
bash /tmp/bun_setup.sh
rm -f /tmp/bun_setup.sh
# Ensure bun is globally accessible
if [[ -x /root/.bun/bin/bun && ! -e /usr/local/bin/bun ]]; then
ln -s /root/.bun/bin/bun /usr/local/bin/bun
fi
echo -e "${GREEN}Bun version: v$(bun --version)${NC}"
}
# Creates a minimal Bun app under /srv/app unless disabled.
setup_sample_app() {
[[ -n "${SKIP_BUN_APP:-}" ]] && return
mkdir -p "$APP_DIR"
if [[ -f "$APP_DIR/server.ts" ]]; then
echo -e "${YELLOW}Sample app directory exists: $APP_DIR (skipping).${NC}"
return
fi
echo -e "${GREEN}Creating sample Bun app at $APP_DIR...${NC}"
download_template "server.ts" "$APP_DIR/server.ts"
download_template "package.json" "$APP_DIR/package.json"
download_template "nginx-index-sample.html" "$NGINX_ROOT/index.html"
chown -R www-data:www-data "$NGINX_ROOT"
# set directories to 755 and files to 644
find "$NGINX_ROOT" -type d -exec chmod 755 {} +
find "$NGINX_ROOT" -type f -exec chmod 644 {} +
}
# Writes a static index page for the default Nginx site.
write_default_nginx_index() {
download_template "nginx-index-default.html" "$NGINX_ROOT/index.html"
}
# Writes the Bun reverse proxy configuration to the default Nginx site.
write_bun_nginx_config() {
download_template "nginx-default.conf" /etc/nginx/sites-available/default
replace_placeholder "/etc/nginx/sites-available/default" "__NGINX_ROOT__" "$NGINX_ROOT"
}
# Deploys the Cloudflare helper templates into the main Nginx directory.
copy_cloudflare_templates() {
echo -e "${GREEN}Copying Cloudflare helper files to /etc/nginx...${NC}"
local nginx_dir="/etc/nginx"
for template in cloudflare-ip-filter.conf cloudflare-update-ips.sh; do
download_template "$template" "$nginx_dir/$template"
done
chmod +x "$nginx_dir/cloudflare-update-ips.sh" || true
}
# Configures Nginx defaults based on whether the Bun sample app is installed.
configure_nginx() {
echo -e "${GREEN}Configuring Nginx default site...${NC}"
if [[ -n "${SKIP_BUN_APP:-}" ]]; then
write_default_nginx_index
else
mkdir -p /etc/nginx/sites-available
write_bun_nginx_config
fi
copy_cloudflare_templates
systemctl enable nginx >/dev/null 2>&1 || true
systemctl restart nginx || true
}
# Creates and enables systemd unit for the Bun app (bun-app).
# Starts/restarts only if /srv/app exists. Idempotent: overwrite-safe.
# Side effects: writes /etc/systemd/system/bun-app.service, daemon-reload, enable, (re)start.
create_systemd_service() {
# Honors: SKIP_BUN_APP -> skip entirely. Idempotent: skips if dir exists.
[[ -n "${SKIP_BUN_APP:-}" ]] && return
echo -e "${GREEN}Configuring systemd service bun-app...${NC}"
# File: /etc/systemd/system/bun-app.service
# Create systemd service file
download_template "bun-app.service" /etc/systemd/system/bun-app.service
replace_placeholder "/etc/systemd/system/bun-app.service" "__INSTANCE_ID__" "$INSTANCE_ID"
systemctl daemon-reload
systemctl enable bun-app >/dev/null 2>&1 || true
# Start only if app folder exists
if [[ -d $APP_DIR ]]; then
systemctl restart bun-app || systemctl start bun-app || true
fi
}
# Emits build and environment metadata to /var/lib/app-info/application.info.
# Values include distro info and Bun version if available.
# Side effects: creates/overwrites metadata file.
write_application_info() {
echo -e "${GREEN}Writing application metadata...${NC}"
local info_dir="/var/lib/app-info"
mkdir -p "$info_dir"
local info_file="$info_dir/application.info"
local application_name="Bun.sh"
local build_date
build_date=$(date +%Y-%m-%d)
local distro
distro=$(lsb_release -s -i 2>/dev/null || echo "unknown")
local distro_release
distro_release=$(lsb_release -s -r 2>/dev/null || echo "unknown")
local distro_codename
distro_codename=$(lsb_release -s -c 2>/dev/null || echo "unknown")
local distro_arch
distro_arch=$(uname -m)
local application_version
application_version=$(bun --version 2>/dev/null || echo "unknown")
download_template "application.info" "$info_file"
replace_placeholder "$info_file" "__BUILD_DATE__" "$build_date"
replace_placeholder "$info_file" "__DISTRO__" "$distro"
replace_placeholder "$info_file" "__DISTRO_RELEASE__" "$distro_release"
replace_placeholder "$info_file" "__DISTRO_CODENAME__" "$distro_codename"
replace_placeholder "$info_file" "__DISTRO_ARCH__" "$distro_arch"
replace_placeholder "$info_file" "__APPLICATION_VERSION__" "$application_version"
}
write_instance_id() {
local INSTANCE_LINE
INSTANCE_LINE="INSTANCE_ID=${INSTANCE_ID}"
if ! grep -Fxq "$INSTANCE_LINE" /etc/environment; then
echo "$INSTANCE_LINE" | tee -a /etc/environment >/dev/null
fi
}
# Adds a helpful MOTD script under /etc/update-motd.d/00-custom.
# Displays access info, common commands, and how to remove the MOTD.
# Side effects: writes executable file used at login.
write_motd() {
# Simple informative MOTD; does not expose passwords
local motd=/etc/update-motd.d/00-custom
if [[ -d $motd ]]; then
echo -e "${YELLOW}MOTD script already exists: $motd (skipping).${NC}"
return
fi
echo -e "${GREEN}Creating MOTD entry at ${motd}...${NC}"
chmod -x /etc/update-motd.d/00-header
chmod -x /etc/update-motd.d/10-help-text
download_template "motd.sh" "$motd"
replace_placeholder "$motd" "__NGINX_ROOT__" "$NGINX_ROOT"
chmod +x "$motd"
}
# Prints a concise summary of installed components and next steps.
# Includes Bun path/version, Nginx presence, bun-app service status, and Certbot hint.
print_summary() {
local bun_path bun_ver nginx_ver service_line
bun_path=$(command -v bun || echo "not found")
bun_ver=$(bun --version 2>/dev/null || echo "unknown")
nginx_ver=$(nginx -v 2>&1 || echo "not found")
if systemctl is-enabled bun-app >/dev/null 2>&1; then
service_line=" * Service 'bun-app' is enabled. View logs: journalctl -u bun-app -f"
else
service_line=""
fi
cat <<EOF
${GREEN}Installation complete.${NC}
* Instance ID: ${INSTANCE_ID}
* Bun: ${bun_path} (${bun_ver})
* Nginx: ${nginx_ver}
${service_line}
* Certbot installed. To obtain a certificate for an Nginx site:
certbot --nginx
EOF
}
main() {
echo -e "${GREEN}Starting Ubuntu Bun installation script...${NC}"
require_root
require_ubuntu
apt_update_upgrade
install_base_packages
configure_ufw
install_certbot
install_bun
mkdir -p "$NGINX_ROOT"
setup_sample_app
configure_nginx
create_systemd_service
write_application_info
write_instance_id
write_motd
print_summary
}
main "$@"