Skip to content

Commit db472fe

Browse files
authored
deploy: resolve npm via agent's embedded runtime in update-release (#184)
1 parent 11b44d9 commit db472fe

5 files changed

Lines changed: 285 additions & 57 deletions

File tree

bin/ci/setup-arch.sh

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ set -euo pipefail
1010
echo "=== [Arch] Installing base CI deps ==="
1111
pacman -Sy --noconfirm --needed git jq sudo 2>&1 | tail -3
1212

13+
echo "=== [Arch] Ensuring iptables backend works ==="
14+
if iptables -w -L OUTPUT -n >/dev/null 2>&1; then
15+
echo " iptables backend OK"
16+
else
17+
IPTABLES_NFT_BIN="$(command -v iptables-nft 2>/dev/null || true)"
18+
if [ -n "$IPTABLES_NFT_BIN" ] && "$IPTABLES_NFT_BIN" -w -L OUTPUT -n >/dev/null 2>&1; then
19+
# Some Arch images ship iptables defaulting to legacy backend without
20+
# ip_tables support. Force nft backend so setup-firewall.sh can apply rules.
21+
ln -sf "$IPTABLES_NFT_BIN" /usr/local/sbin/iptables
22+
ln -sf "$IPTABLES_NFT_BIN" /usr/local/bin/iptables
23+
echo " iptables legacy unavailable; forced iptables-nft shim"
24+
else
25+
echo "❌ No working iptables backend found" >&2
26+
exit 1
27+
fi
28+
fi
29+
1330
echo "=== Preparing source ==="
1431
useradd -m -s /bin/bash baudbot_admin
1532
mkdir -p /home/baudbot_admin/baudbot
@@ -29,8 +46,10 @@ BAUDBOT_BOOTSTRAP_TARGET="/usr/local/bin/baudbot" \
2946
# Prompts: admin user, LLM choice(1=Anthropic), Anthropic key,
3047
# Slack mode(2=advanced), Slack bot, Slack app, Slack users,
3148
# Browser?(n), Sentry?(n), launch(n)
49+
# Arch CI droplets frequently lack netfilter modules required by setup-firewall;
50+
# skip firewall bootstrap here to keep install/integration coverage stable.
3251
printf 'baudbot_admin\n1\nsk-ant-testkey\n2\nxoxb-test\nxapp-test\nU01TEST\nn\nn\nn\n' \
33-
| BAUDBOT_INSTALL_SCRIPT_URL="file:///home/baudbot_admin/baudbot/install.sh" baudbot install
52+
| BAUDBOT_SKIP_FIREWALL=1 BAUDBOT_INSTALL_SCRIPT_URL="file:///home/baudbot_admin/baudbot/install.sh" baudbot install
3453

3554
echo "=== Verifying install ==="
3655
# .env exists with correct permissions

bin/setup-firewall.sh

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,37 @@ if [ -z "$UID_BAUDBOT" ]; then
3333
fi
3434

3535
CHAIN="BAUDBOT_OUTPUT"
36+
IPTABLES_BIN="iptables"
37+
if command -v iptables-nft >/dev/null 2>&1 && iptables-nft -w -L OUTPUT -n >/dev/null 2>&1; then
38+
IPTABLES_BIN="iptables-nft"
39+
fi
40+
41+
fw() {
42+
"$IPTABLES_BIN" -w "$@"
43+
}
44+
45+
add_optional_rule() {
46+
if ! fw "$@"; then
47+
echo "⚠️ Optional firewall rule unsupported by kernel, skipping: $IPTABLES_BIN -w $*" >&2
48+
fi
49+
}
3650

3751
echo "🔒 Setting up firewall rules for $BAUDBOT_AGENT_USER (uid $UID_BAUDBOT)..."
52+
echo " backend: $IPTABLES_BIN"
3853

3954
# Clean up any existing rules first
40-
iptables -w -D OUTPUT -m owner --uid-owner "$UID_BAUDBOT" -j "$CHAIN" 2>/dev/null || true
41-
iptables -w -F "$CHAIN" 2>/dev/null || true
42-
iptables -w -X "$CHAIN" 2>/dev/null || true
55+
fw -D OUTPUT -m owner --uid-owner "$UID_BAUDBOT" -j "$CHAIN" 2>/dev/null || true
56+
fw -F "$CHAIN" 2>/dev/null || true
57+
fw -X "$CHAIN" 2>/dev/null || true
4358

4459
# Create a dedicated chain for baudbot_agent
45-
iptables -w -N "$CHAIN"
60+
fw -N "$CHAIN"
4661

4762
# ── Logging (SYN + DNS only — low volume) ────────────────────────────────────
48-
# Log all new outbound connections (SYN packets only to avoid flooding)
49-
iptables -w -A "$CHAIN" -p tcp --syn -j LOG --log-prefix "baudbot-out: " --log-level info
50-
# Log DNS queries
51-
iptables -w -A "$CHAIN" -p udp --dport 53 -j LOG --log-prefix "baudbot-dns: " --log-level info
63+
# Some kernels (notably certain cloud Arch images) lack optional LOG/tcp xtables
64+
# modules. Treat logging rules as best-effort; the allow/drop policy is mandatory.
65+
add_optional_rule -A "$CHAIN" -p tcp --syn -j LOG --log-prefix "baudbot-out: " --log-level info
66+
add_optional_rule -A "$CHAIN" -p udp --dport 53 -j LOG --log-prefix "baudbot-dns: " --log-level info
5267

5368
# ── Localhost: allow only specific services ──────────────────────────────────
5469

@@ -57,76 +72,76 @@ iptables -w -A "$CHAIN" -p udp --dport 53 -j LOG --log-prefix "baudbot-dns: " --
5772
# 4000-4999: Astro (4321), Remix (4200), Nuxt, etc.
5873
# 5000-5999: Vite (5173), Flask, generic dev servers
5974
# 6000-6099: Storybook (6006), Expo (6100 range is X11 — skip 6063+)
60-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 3000:5999 -j ACCEPT
61-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 6006 -j ACCEPT
75+
fw -A "$CHAIN" -o lo -p tcp --dport 3000:5999 -j ACCEPT
76+
fw -A "$CHAIN" -o lo -p tcp --dport 6006 -j ACCEPT
6277

6378
# ── Databases ────────────────────────────────────────────────────────────
6479
# 5432: PostgreSQL (native)
6580
# 6379: Redis
6681
# 27017: MongoDB
6782
# 54322: PostgreSQL (Docker-mapped)
68-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 5432 -j ACCEPT
69-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 6379 -j ACCEPT
70-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 27017 -j ACCEPT
71-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 54322 -j ACCEPT
83+
fw -A "$CHAIN" -o lo -p tcp --dport 5432 -j ACCEPT
84+
fw -A "$CHAIN" -o lo -p tcp --dport 6379 -j ACCEPT
85+
fw -A "$CHAIN" -o lo -p tcp --dport 27017 -j ACCEPT
86+
fw -A "$CHAIN" -o lo -p tcp --dport 54322 -j ACCEPT
7287

7388
# ── Infrastructure ───────────────────────────────────────────────────────
7489
# 7890: Slack bridge
7590
# 8000-9999: Wrangler (8787), Django/FastAPI (8000), inspector (9229+), MinIO (9000)
7691
# 11434: Ollama
7792
# 24678: Vite HMR websocket
78-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 7890 -j ACCEPT
79-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 8000:9999 -j ACCEPT
80-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 11434 -j ACCEPT
81-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 24678 -j ACCEPT
93+
fw -A "$CHAIN" -o lo -p tcp --dport 7890 -j ACCEPT
94+
fw -A "$CHAIN" -o lo -p tcp --dport 8000:9999 -j ACCEPT
95+
fw -A "$CHAIN" -o lo -p tcp --dport 11434 -j ACCEPT
96+
fw -A "$CHAIN" -o lo -p tcp --dport 24678 -j ACCEPT
8297

8398
# Allow DNS on localhost
84-
iptables -w -A "$CHAIN" -o lo -p udp --dport 53 -j ACCEPT
85-
iptables -w -A "$CHAIN" -o lo -p tcp --dport 53 -j ACCEPT
99+
fw -A "$CHAIN" -o lo -p udp --dport 53 -j ACCEPT
100+
fw -A "$CHAIN" -o lo -p tcp --dport 53 -j ACCEPT
86101

87102
# Allow localhost responses (established connections back to us)
88-
iptables -w -A "$CHAIN" -o lo -m state --state ESTABLISHED,RELATED -j ACCEPT
103+
fw -A "$CHAIN" -o lo -m state --state ESTABLISHED,RELATED -j ACCEPT
89104

90105
# Block everything else on localhost
91-
iptables -w -A "$CHAIN" -o lo -j LOG --log-prefix "BAUDBOT_LOCAL_BLOCKED: " --log-level 4
92-
iptables -w -A "$CHAIN" -o lo -j DROP
106+
add_optional_rule -A "$CHAIN" -o lo -j LOG --log-prefix "BAUDBOT_LOCAL_BLOCKED: " --log-level 4
107+
fw -A "$CHAIN" -o lo -j DROP
93108

94109
# ── Internet: allow standard + dev ports ─────────────────────────────────────
95110

96111
# DNS (UDP + TCP)
97-
iptables -w -A "$CHAIN" -p udp --dport 53 -j ACCEPT
98-
iptables -w -A "$CHAIN" -p tcp --dport 53 -j ACCEPT
112+
fw -A "$CHAIN" -p udp --dport 53 -j ACCEPT
113+
fw -A "$CHAIN" -p tcp --dport 53 -j ACCEPT
99114

100115
# HTTP/HTTPS (web, APIs, cloud services)
101-
iptables -w -A "$CHAIN" -p tcp --dport 80 -j ACCEPT
102-
iptables -w -A "$CHAIN" -p tcp --dport 443 -j ACCEPT
116+
fw -A "$CHAIN" -p tcp --dport 80 -j ACCEPT
117+
fw -A "$CHAIN" -p tcp --dport 443 -j ACCEPT
103118

104119
# SSH (git push/pull)
105-
iptables -w -A "$CHAIN" -p tcp --dport 22 -j ACCEPT
120+
fw -A "$CHAIN" -p tcp --dport 22 -j ACCEPT
106121

107122
# Cloud databases (Neon, Supabase, RDS, PlanetScale, Atlas, Upstash, etc.)
108-
iptables -w -A "$CHAIN" -p tcp --dport 3306 -j ACCEPT # MySQL / PlanetScale
109-
iptables -w -A "$CHAIN" -p tcp --dport 5432:5433 -j ACCEPT # PostgreSQL / Neon
110-
iptables -w -A "$CHAIN" -p tcp --dport 6543 -j ACCEPT # Supabase pooler
111-
iptables -w -A "$CHAIN" -p tcp --dport 6379 -j ACCEPT # Redis Cloud / Upstash
112-
iptables -w -A "$CHAIN" -p tcp --dport 27017 -j ACCEPT # MongoDB Atlas
123+
fw -A "$CHAIN" -p tcp --dport 3306 -j ACCEPT # MySQL / PlanetScale
124+
fw -A "$CHAIN" -p tcp --dport 5432:5433 -j ACCEPT # PostgreSQL / Neon
125+
fw -A "$CHAIN" -p tcp --dport 6543 -j ACCEPT # Supabase pooler
126+
fw -A "$CHAIN" -p tcp --dport 6379 -j ACCEPT # Redis Cloud / Upstash
127+
fw -A "$CHAIN" -p tcp --dport 27017 -j ACCEPT # MongoDB Atlas
113128

114129
# Observability (OpenTelemetry OTLP)
115-
iptables -w -A "$CHAIN" -p tcp --dport 4317:4318 -j ACCEPT
130+
fw -A "$CHAIN" -p tcp --dport 4317:4318 -j ACCEPT
116131

117132
# Allow established/related (responses to allowed outbound)
118-
iptables -w -A "$CHAIN" -m state --state ESTABLISHED,RELATED -j ACCEPT
133+
fw -A "$CHAIN" -m state --state ESTABLISHED,RELATED -j ACCEPT
119134

120135
# Log and drop everything else
121-
iptables -w -A "$CHAIN" -j LOG --log-prefix "BAUDBOT_BLOCKED: " --log-level 4
122-
iptables -w -A "$CHAIN" -j DROP
136+
add_optional_rule -A "$CHAIN" -j LOG --log-prefix "BAUDBOT_BLOCKED: " --log-level 4
137+
fw -A "$CHAIN" -j DROP
123138

124139
# Jump to our chain for all baudbot_agent traffic
125-
iptables -w -A OUTPUT -m owner --uid-owner "$UID_BAUDBOT" -j "$CHAIN"
140+
fw -A OUTPUT -m owner --uid-owner "$UID_BAUDBOT" -j "$CHAIN"
126141

127142
echo "✅ Firewall active. Rules:"
128143
echo ""
129-
iptables -w -L "$CHAIN" -n -v --line-numbers
144+
fw -L "$CHAIN" -n -v --line-numbers
130145
echo ""
131146
echo "Localhost allowed: 3000-5999 (dev servers), 5432 (pg), 6006 (storybook),"
132147
echo " 6379 (redis), 7890 (bridge), 8000-9999 (wrangler/inspector),"
@@ -137,6 +152,6 @@ echo " 3306 (mysql), 4317-4318 (otlp), 5432-5433 (pg),"
137152
echo " 6379 (redis), 6543 (supabase), 27017 (mongo)"
138153
echo "Everything else: BLOCKED + LOGGED"
139154
echo ""
140-
echo "To remove: sudo iptables -w -D OUTPUT -m owner --uid-owner $UID_BAUDBOT -j $CHAIN && sudo iptables -w -F $CHAIN && sudo iptables -w -X $CHAIN"
155+
echo "To remove: sudo $IPTABLES_BIN -w -D OUTPUT -m owner --uid-owner $UID_BAUDBOT -j $CHAIN && sudo $IPTABLES_BIN -w -F $CHAIN && sudo $IPTABLES_BIN -w -X $CHAIN"
141156
echo ""
142157
echo "Persistence: baudbot-firewall.service (systemd)"

bin/update-release.sh

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,53 @@ source "$SCRIPT_DIR/lib/release-runtime-common.sh"
5555
# shellcheck source=bin/lib/json-common.sh
5656
source "$SCRIPT_DIR/lib/json-common.sh"
5757

58+
# ---------------------------------------------------------------------------
59+
# Resolve the full path to npm. This script runs as root (sudo) where the
60+
# embedded Node runtime is *not* on PATH. Resolution order:
61+
# 1. Agent user's embedded runtime (/home/baudbot_agent/opt/node/bin/npm)
62+
# 2. SUDO_USER's home (admin may have mise/nvm/etc.)
63+
# 3. Standard PATH lookup (last resort)
64+
# ---------------------------------------------------------------------------
65+
resolve_npm_bin() {
66+
local candidate=""
67+
68+
# 1) Agent's embedded runtime
69+
local agent_home="/home/${BAUDBOT_AGENT_USER:-baudbot_agent}"
70+
candidate="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
71+
if [ -n "$candidate" ] && [ -x "$candidate/npm" ]; then
72+
echo "$candidate/npm"
73+
return 0
74+
fi
75+
76+
# 2) SUDO_USER's home (admin's local node install — mise, nvm, etc.)
77+
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
78+
local sudo_home=""
79+
sudo_home="$(bb_resolve_user_home "$SUDO_USER" 2>/dev/null || true)"
80+
if [ -n "$sudo_home" ]; then
81+
local dir=""
82+
for dir in \
83+
"$sudo_home/.local/share/mise/shims" \
84+
"$sudo_home/.nvm/versions/node"/*/bin \
85+
"$sudo_home/.local/bin"; do
86+
# Skip unexpanded globs.
87+
case "$dir" in *\**) continue ;; esac
88+
if [ -x "$dir/npm" ]; then
89+
echo "$dir/npm"
90+
return 0
91+
fi
92+
done
93+
fi
94+
fi
95+
96+
# 3) PATH (may work in non-sudo environments or CI)
97+
if command -v npm >/dev/null 2>&1; then
98+
command -v npm
99+
return 0
100+
fi
101+
102+
return 1
103+
}
104+
58105
cleanup() {
59106
if [ -n "$CHECKOUT_DIR" ] && [ -d "$CHECKOUT_DIR" ]; then
60107
rm -rf "$CHECKOUT_DIR"
@@ -221,14 +268,11 @@ install_release_bridge_dependencies() {
221268
log "installing production Slack bridge dependencies in release"
222269
rm -rf "$bridge_dir/node_modules"
223270

224-
# Resolve npm from the agent's embedded node runtime, falling back to PATH.
225-
local npm_bin="npm"
226-
local agent_home="/home/${BAUDBOT_AGENT_USER:-baudbot_agent}"
227-
local node_bin_dir=""
228-
node_bin_dir="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
229-
if [ -n "$node_bin_dir" ] && [ -x "$node_bin_dir/npm" ]; then
230-
npm_bin="$node_bin_dir/npm"
231-
fi
271+
# Resolve npm via the embedded Node runtime. update-release runs as root
272+
# (sudo) so bare `npm` / `node` will not be on PATH — we must resolve the
273+
# full path. Try: agent home → SUDO_USER home → PATH (last resort).
274+
local npm_bin=""
275+
npm_bin="$(resolve_npm_bin)" || die "cannot find npm; install Node for the agent (see setup.sh) or ensure npm is on PATH"
232276

233277
if [ -f "$bridge_dir/package-lock.json" ]; then
234278
(cd "$bridge_dir" && "$npm_bin" ci --omit=dev)

0 commit comments

Comments
 (0)