@kvbox/server 0.5.9 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kvbox-server.js +259 -45
- package/dist/kvbox-server.js.map +1 -1
- package/package.json +1 -1
- package/scripts/net-setup.sh +57 -6
package/package.json
CHANGED
package/scripts/net-setup.sh
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Host networking for kvbox: bridge kvbox0 (172.30.0.1/24), IP forwarding, NAT to the
|
|
3
3
|
# default route, and FORWARD accept rules for the bridge (Docker sets FORWARD's policy
|
|
4
|
-
# to DROP, so NAT alone is not enough)
|
|
4
|
+
# to DROP, so NAT alone is not enough), and the guard around boxes: no box to box, box to
|
|
5
|
+
# host, box to private networks or outbound mail. Needs root; re-execs itself with sudo.
|
|
5
6
|
# Idempotent. The bridge and the rules vanish on host reboot, so this runs from
|
|
6
7
|
# install-server.sh once and from kvbox-server at start whenever the bridge is missing.
|
|
7
8
|
#
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
# keep the tunnel to the relay up, now and after reboots (wg-quick@kvbox), and
|
|
22
23
|
# let the control plane's HTTPS port (default 7777) in on the tunnel interface
|
|
23
24
|
# (a host firewall such as ufw would otherwise drop it; box ports are forwarded)
|
|
25
|
+
# net-setup.sh relay restart redial the tunnel (kvbox-server does it when the relay stops answering)
|
|
24
26
|
# net-setup.sh relay down take the tunnel down and remove the conf
|
|
25
27
|
# net-setup.sh relay status is the tunnel up, when did the relay last answer
|
|
26
28
|
#
|
|
@@ -51,10 +53,10 @@ case "$action" in
|
|
|
51
53
|
proxy) [[ "$px_op" =~ ^(add|rm)$ && "$px_box" =~ ^[a-z0-9][a-z0-9._-]{0,14}$ && "$px_ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ \
|
|
52
54
|
&& "$px_hport" =~ ^[0-9]{1,5}$ && "$px_gport" =~ ^[0-9]{1,5}$ && "$px_hport" -ne 22 && "$px_proto" =~ ^(tcp|udp)$ ]] \
|
|
53
55
|
|| fail "usage: $0 proxy add|rm <box-id> <box-ip> <host-port> <guest-port> [tcp|udp] (host port is never 22)" ;;
|
|
54
|
-
relay) if [[ ! "$relay_op" =~ ^(up|down|status)$ ]] || { [[ "$relay_op" == up ]] && [[ -z "$relay_conf" ]]; } || [[ ! "$relay_port" =~ ^[0-9]{1,5}$ ]]; then
|
|
55
|
-
fail "usage: $0 relay up <conf> [port] | relay down | relay status"
|
|
56
|
+
relay) if [[ ! "$relay_op" =~ ^(up|restart|down|status)$ ]] || { [[ "$relay_op" == up ]] && [[ -z "$relay_conf" ]]; } || [[ ! "$relay_port" =~ ^[0-9]{1,5}$ ]]; then
|
|
57
|
+
fail "usage: $0 relay up <conf> [port] | relay restart | relay down | relay status"
|
|
56
58
|
fi ;;
|
|
57
|
-
*) fail "usage: $0 [up|down|status|tap add|attach|del <name>|proxy add|rm ...|relay up|down|status]" ;;
|
|
59
|
+
*) fail "usage: $0 [up|down|status|tap add|attach|del <name>|proxy add|rm ...|relay up|restart|down|status]" ;;
|
|
58
60
|
esac
|
|
59
61
|
for bin in ip iptables sysctl; do command -v "$bin" >/dev/null 2>&1 || fail "$bin not found"; done
|
|
60
62
|
if [[ "$action" == relay ]]; then
|
|
@@ -78,6 +80,11 @@ fi
|
|
|
78
80
|
# ── rule set ─────────────────────────────────────────────────────────────────
|
|
79
81
|
# RULE_DESC[i] is what we tell the user; RULE_SPEC[i] is "<table> <chain> <rule...>".
|
|
80
82
|
# Added with -I (ahead of Docker's chains), checked with -C, found again by the comment tag.
|
|
83
|
+
# Each rule is inserted at the top, so a later entry ends up above an earlier one: the replies
|
|
84
|
+
# accept comes last so it sits above the drops, and the drops sit above the general accept.
|
|
85
|
+
# Boxes are strangers to each other and to this host (box to box is cut at the bridge, see
|
|
86
|
+
# do_tap_attach): a box reaches the internet only, never the host, its LAN or cloud metadata.
|
|
87
|
+
PRIVATE_NETS=(10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10 169.254.0.0/16)
|
|
81
88
|
RULE_DESC=(
|
|
82
89
|
"NAT: boxes reach the internet using this host's address"
|
|
83
90
|
"firewall: traffic from boxes may leave through $BRIDGE"
|
|
@@ -88,6 +95,22 @@ RULE_SPEC=(
|
|
|
88
95
|
"filter FORWARD -i $BRIDGE -m comment --comment $TAG -j ACCEPT"
|
|
89
96
|
"filter FORWARD -o $BRIDGE -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment $TAG -j ACCEPT"
|
|
90
97
|
)
|
|
98
|
+
for net in "${PRIVATE_NETS[@]}"; do
|
|
99
|
+
RULE_DESC+=("firewall: boxes cannot reach private network $net (this host's LAN, provider networks, cloud metadata)")
|
|
100
|
+
RULE_SPEC+=("filter FORWARD -i $BRIDGE -d $net -m comment --comment $TAG -j DROP")
|
|
101
|
+
done
|
|
102
|
+
RULE_DESC+=(
|
|
103
|
+
"firewall: boxes cannot send mail directly (port 25), so spam never gets this host's address blocked"
|
|
104
|
+
"firewall: boxes may answer connections made to them (published ports, the relay)"
|
|
105
|
+
"firewall: boxes cannot reach this host itself"
|
|
106
|
+
"firewall: this host may still talk to its boxes"
|
|
107
|
+
)
|
|
108
|
+
RULE_SPEC+=(
|
|
109
|
+
"filter FORWARD -i $BRIDGE -p tcp --dport 25 -m comment --comment $TAG -j DROP"
|
|
110
|
+
"filter FORWARD -i $BRIDGE -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment $TAG -j ACCEPT"
|
|
111
|
+
"filter INPUT -i $BRIDGE -m comment --comment $TAG -j DROP"
|
|
112
|
+
"filter INPUT -i $BRIDGE -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment $TAG -j ACCEPT"
|
|
113
|
+
)
|
|
91
114
|
has_rule() { local t=$1 c=$2; shift 2; iptables -t "$t" -C "$c" "$@" 2>/dev/null; }
|
|
92
115
|
add_rule() { local t=$1 c=$2; shift 2; iptables -t "$t" -I "$c" 1 "$@"; }
|
|
93
116
|
del_rule() { local t=$1 c=$2; shift 2; while iptables -t "$t" -C "$c" "$@" 2>/dev/null; do iptables -t "$t" -D "$c" "$@"; done; }
|
|
@@ -104,11 +127,24 @@ do_up() {
|
|
|
104
127
|
if bridge_has_ip; then skip "$BRIDGE already has address $GATEWAY"; else ip addr add "$GATEWAY/$PREFIX" dev "$BRIDGE"; ok "gave this host address $GATEWAY on $BRIDGE (boxes use it as their gateway)"; fi
|
|
105
128
|
if bridge_is_up; then skip "$BRIDGE is already up"; else ip link set "$BRIDGE" up; ok "brought $BRIDGE up"; fi
|
|
106
129
|
if [[ "$(sysctl -n net.ipv4.ip_forward)" == 1 ]]; then skip "IP forwarding already enabled"; else sysctl -q -w net.ipv4.ip_forward=1; ok "enabled IP forwarding (this host routes for the boxes)"; fi
|
|
107
|
-
local i
|
|
130
|
+
local i missing=0 present=()
|
|
108
131
|
for i in "${!RULE_SPEC[@]}"; do
|
|
109
132
|
# shellcheck disable=SC2086 # the rule is a word list by design
|
|
110
|
-
if has_rule ${RULE_SPEC[$i]}; then
|
|
133
|
+
if has_rule ${RULE_SPEC[$i]}; then present[i]=1; else missing=1; fi
|
|
111
134
|
done
|
|
135
|
+
# Order matters (accepts above drops), and a rule inserted alone would land on top of the others:
|
|
136
|
+
# when anything is missing, take the set out and put it all back in order.
|
|
137
|
+
if [[ $missing -eq 1 ]]; then
|
|
138
|
+
# shellcheck disable=SC2086
|
|
139
|
+
for i in "${!RULE_SPEC[@]}"; do del_rule ${RULE_SPEC[$i]}; done
|
|
140
|
+
fi
|
|
141
|
+
for i in "${!RULE_SPEC[@]}"; do
|
|
142
|
+
# shellcheck disable=SC2086
|
|
143
|
+
[[ $missing -eq 1 ]] && add_rule ${RULE_SPEC[$i]}
|
|
144
|
+
if [[ -n "${present[i]:-}" ]]; then skip "${RULE_DESC[$i]} (already set)"; else ok "${RULE_DESC[$i]}"; fi
|
|
145
|
+
done
|
|
146
|
+
local tap
|
|
147
|
+
for tap in $(taps_on_bridge); do isolate_tap "$tap"; done
|
|
112
148
|
echo
|
|
113
149
|
ok "host network ready: boxes get addresses in $SUBNET on $BRIDGE, gateway $GATEWAY, internet via NAT"
|
|
114
150
|
}
|
|
@@ -152,6 +188,8 @@ do_status() {
|
|
|
152
188
|
done
|
|
153
189
|
local taps; taps="$(taps_on_bridge)"
|
|
154
190
|
if [[ -z "$taps" ]]; then skip "no boxes attached to $BRIDGE yet"; else ok "boxes attached to $BRIDGE: $taps"; fi
|
|
191
|
+
local tap
|
|
192
|
+
for tap in $taps; do tap_isolated "$tap" || bad "box on $tap can reach other boxes (tap not isolated)"; done
|
|
155
193
|
local fwds; fwds="$(proxy_summary)"
|
|
156
194
|
if [[ -z "$fwds" ]]; then skip "no box ports published on this host"; else ok "box ports published on this host:"; printf '%s\n' "$fwds"; fi
|
|
157
195
|
if [[ $missing -eq 0 ]]; then echo; ok "host network is ready"; else echo; printf '%s✗%s host network incomplete; run: kvbox-server net up\n' "$RED" "$NC"; fi
|
|
@@ -180,8 +218,13 @@ do_tap_attach() {
|
|
|
180
218
|
local tap=$1
|
|
181
219
|
[[ -d "/sys/class/net/$tap" ]] || fail "tap $tap does not exist"
|
|
182
220
|
ip link set "$tap" master "$BRIDGE" up
|
|
221
|
+
isolate_tap "$tap"
|
|
183
222
|
ok "tap $tap plugged into $BRIDGE"
|
|
184
223
|
}
|
|
224
|
+
# An isolated bridge port talks to the host (the gateway) but never to another isolated port:
|
|
225
|
+
# one box cannot see another box's ports, whoever owns them.
|
|
226
|
+
isolate_tap() { ip link set dev "$1" type bridge_slave isolated on; }
|
|
227
|
+
tap_isolated() { ip -d link show dev "$1" 2>/dev/null | grep -q "isolated on"; }
|
|
185
228
|
do_tap_del() {
|
|
186
229
|
local tap=$1
|
|
187
230
|
if [[ -d "/sys/class/net/$tap" ]]; then ip link del "$tap"; ok "deleted tap $tap"; else skip "tap $tap does not exist"; fi
|
|
@@ -268,6 +311,13 @@ do_relay_up() {
|
|
|
268
311
|
eval "iptables -t filter -D $rule" && ok "firewall: closed the tunnel's old port $(sed -E 's/.*--dport ([0-9]+) .*/\1/' <<<"$rule")"
|
|
269
312
|
done < <(relay_input_rules)
|
|
270
313
|
}
|
|
314
|
+
# A new process dials from a new UDP port, which a home router maps afresh: the way out of a mapping
|
|
315
|
+
# that went stale while the relay rebooted (the relay hears us but cannot answer).
|
|
316
|
+
do_relay_restart() {
|
|
317
|
+
[[ -f "$relay_target" ]] || fail "no relay tunnel configured on this host"
|
|
318
|
+
systemctl restart "$relay_unit" || fail "could not restart the tunnel (journalctl -u $relay_unit)"
|
|
319
|
+
ok "tunnel $RELAY_IF restarted"
|
|
320
|
+
}
|
|
271
321
|
do_relay_down() {
|
|
272
322
|
local rule n=0
|
|
273
323
|
while IFS= read -r rule; do
|
|
@@ -312,6 +362,7 @@ case "$action" in
|
|
|
312
362
|
esac ;;
|
|
313
363
|
relay) case "$relay_op" in
|
|
314
364
|
up) do_relay_up "$relay_conf" "$relay_port" ;;
|
|
365
|
+
restart) do_relay_restart ;;
|
|
315
366
|
down) do_relay_down ;;
|
|
316
367
|
status) do_relay_status ;;
|
|
317
368
|
esac ;;
|