mirror of
https://codeberg.org/emmett1/wg-connect
synced 2026-08-16 11:12:04 +08:00
initial commit
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Emmett1
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,18 @@
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man/man1
|
||||
INSTALL ?= install
|
||||
INSTALL_FL = -m 0755
|
||||
INSTALL_FM = -m 0644
|
||||
|
||||
.PHONY: install uninstall
|
||||
|
||||
install:
|
||||
$(INSTALL) -d $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -d $(DESTDIR)$(MANDIR)
|
||||
$(INSTALL) $(INSTALL_FL) wg-connect $(DESTDIR)$(BINDIR)/wg-connect
|
||||
$(INSTALL) $(INSTALL_FM) wg-connect.1 $(DESTDIR)$(MANDIR)/wg-connect.1
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/wg-connect
|
||||
rm -f $(DESTDIR)$(MANDIR)/wg-connect.1
|
||||
@@ -0,0 +1,122 @@
|
||||
# wg-connect
|
||||
|
||||
Bring a WireGuard tunnel up and down on BusyBox-based systems without
|
||||
`wg-quick`, systemd, or bash.
|
||||
|
||||
## Requirements
|
||||
|
||||
- `wg` - WireGuard userspace tool
|
||||
- `ip` - iproute2 or BusyBox `ip`
|
||||
- `grep` - BusyBox grep (with `-E` support)
|
||||
- POSIX `/bin/sh`
|
||||
|
||||
Root privileges are required (the script creates and configures network
|
||||
interfaces).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
make install # to /usr/local
|
||||
make install PREFIX=/usr # to /usr
|
||||
make install DESTDIR=/tmp/pkg # for packaging
|
||||
```
|
||||
|
||||
Or manually:
|
||||
|
||||
```sh
|
||||
install -m 0755 wg-connect /usr/local/bin/wg-connect
|
||||
install -m 0644 wg-connect.1 /usr/local/share/man/man1/wg-connect.1
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
wg-connect up <config> # bring a tunnel up
|
||||
wg-connect down [name] # tear a tunnel down
|
||||
```
|
||||
|
||||
**`up`** requires a config argument, resolved as follows:
|
||||
|
||||
| Argument | Resolves to |
|
||||
|---|---|
|
||||
| `myvpn` | `/etc/wireguard/myvpn.conf` |
|
||||
| `myvpn.conf` | `./myvpn.conf`, then `/etc/wireguard/myvpn.conf` |
|
||||
| `./foo.conf` | `./foo.conf` (path with `/` - used as-is) |
|
||||
| `/any/path/c.conf` | `/any/path/c.conf` (absolute path) |
|
||||
|
||||
The interface name is derived from the config file's basename (minus
|
||||
`.conf`). For example, `wg-connect up myvpn` creates interface `myvpn`,
|
||||
and `wg-connect up /etc/wireguard/home.conf` creates interface `home`.
|
||||
|
||||
**`down`** accepts an optional name to tear down a specific tunnel.
|
||||
Without a name it defaults to `wg0`.
|
||||
|
||||
## Config format
|
||||
|
||||
Standard WireGuard `[Interface]` + `[Peer]` sections. `wg-quick`
|
||||
extensions (`Address`, `DNS`, etc.) are accepted. The script strips
|
||||
them before passing the config to `wg setconf` and handles them itself.
|
||||
|
||||
```ini
|
||||
[Interface]
|
||||
Address = 10.0.0.2/24
|
||||
PrivateKey = oBKGh1W0UeO7R2aV5pLkdMn8Xq3TcFyRbzJwZsPvCg=
|
||||
DNS = 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
PublicKey = xTIBA5rboUvnH4htjbDs6TFeFHS4k0mrKV4xJCzO0H8=
|
||||
PresharedKey = /pUcv4j6DZ1UGm0PwR7aBr9Lk2sFdXq3OcVy5Jh8Tg=
|
||||
Endpoint = 203.0.113.45:51820
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
```
|
||||
|
||||
Multiple `[Peer]` sections are supported; `AllowedIPs` and `Endpoint`
|
||||
values are accumulated across all of them.
|
||||
|
||||
Note: if you run multiple tunnels, each must have a unique `ListenPort`
|
||||
(or omit it entirely). The script detects port conflicts before
|
||||
setting up the interface.
|
||||
|
||||
## What it does
|
||||
|
||||
**`up`**
|
||||
|
||||
1. Resolves the config path and derives the interface name.
|
||||
2. Checks for port conflicts with any existing WireGuard interface.
|
||||
3. Parses the config for `Address`, `DNS`, `Endpoint`, `AllowedIPs`,
|
||||
and `ListenPort`.
|
||||
4. Adds explicit routes for each peer endpoint through the current
|
||||
default gateway, so encrypted UDP packets are not caught in the
|
||||
tunnel's own routing.
|
||||
5. Creates the WireGuard interface and applies the config.
|
||||
6. Assigns the `Address` (appending `/32` if no CIDR is given).
|
||||
7. Brings the interface up.
|
||||
8. Installs routes for each `AllowedIPs` entry. `0.0.0.0/0` replaces
|
||||
the default route.
|
||||
9. If `DNS` is set, backs up `/etc/resolv.conf` and writes the VPN
|
||||
nameserver.
|
||||
10. Saves state to `/tmp/wg-connect.<iface>.state` for teardown.
|
||||
|
||||
If any step fails, the trap handler rolls back everything created so
|
||||
far (interface, endpoint routes, state file).
|
||||
|
||||
**`down`**
|
||||
|
||||
1. Reads `/tmp/wg-connect.<name>.state`.
|
||||
2. Restores the original `/etc/resolv.conf`.
|
||||
3. Removes the endpoint-specific routes.
|
||||
4. Deletes the interface (which also removes its addresses and routes).
|
||||
5. Restores the original default route if one was saved.
|
||||
6. Removes the state file.
|
||||
|
||||
If the state file is missing but the interface still exists, `down`
|
||||
falls back to cleaning up the leftover interface.
|
||||
|
||||
## Limitations
|
||||
|
||||
- IPv6 addresses and routes are skipped (BusyBox `ip` may lack `-6`
|
||||
support).
|
||||
- `DNS` supports a single nameserver only.
|
||||
- PostUp / PostDown / PreUp / PreDown hooks are not executed.
|
||||
- No firewall rules are added. If you need a kill switch, configure
|
||||
`iptables` separately.
|
||||
Executable
+362
@@ -0,0 +1,362 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# wg-connect - bring a WireGuard tunnel up/down on BusyBox systems.
|
||||
# Usage: wg-connect up [config] (defaults to ./peer.conf)
|
||||
# wg-connect down
|
||||
#
|
||||
# Requires: wg, ip, grep, sed, cut (all typically available on BusyBox).
|
||||
|
||||
set -e
|
||||
|
||||
STATE_FILE="/tmp/wg-connect.state"
|
||||
IFACE_DEFAULT="wg0"
|
||||
|
||||
usage() {
|
||||
echo "Usage: wg-connect up <config> | wg-connect down [name]" >&2
|
||||
echo " up <config> config name (looked up in /etc/wireguard/<name>.conf)" >&2
|
||||
echo " up <path> full or relative path to a .conf file" >&2
|
||||
echo " down [name] tear down a tunnel (default: wg0)" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "wg-connect: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Resolve a config argument to a path:
|
||||
# - empty → usage error
|
||||
# - contains / → use as-is (already a path)
|
||||
# - *.conf → try cwd, then /etc/wireguard/
|
||||
# - plain name → /etc/wireguard/<name>.conf
|
||||
resolve_config() {
|
||||
_name="$1"
|
||||
|
||||
[ -z "$_name" ] && { usage; exit 1; }
|
||||
|
||||
# Full or relative path given
|
||||
case "$_name" in
|
||||
*/* ) echo "$_name"; return 0 ;;
|
||||
esac
|
||||
|
||||
# Explicit .conf filename - try cwd first, then /etc/wireguard
|
||||
case "$_name" in
|
||||
*.conf )
|
||||
[ -f "$_name" ] && { echo "$_name"; return 0; }
|
||||
[ -f "/etc/wireguard/$_name" ] && { echo "/etc/wireguard/$_name"; return 0; }
|
||||
die "config not found: $_name (tried . and /etc/wireguard/)"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Bare name → /etc/wireguard/<name>.conf
|
||||
_path="/etc/wireguard/${_name}.conf"
|
||||
[ -f "$_path" ] || die "config not found: $_path"
|
||||
echo "$_path"
|
||||
}
|
||||
|
||||
# -- config parser -------------------------------------------------------
|
||||
# Reads a WireGuard .conf file and sets global vars:
|
||||
# IFACE, ADDRESS, DNS, LISTEN_PORT, PRIVATE_KEY
|
||||
# Each Peer is stored as positional-ish vars: we only extract what we need
|
||||
# for route/DNS handling, since wg setconf reads the raw config directly.
|
||||
|
||||
parse_config() {
|
||||
_cfg="$1"
|
||||
|
||||
[ -f "$_cfg" ] || die "config file not found: $_cfg"
|
||||
|
||||
# Reset globals
|
||||
ADDRESS=""
|
||||
DNS=""
|
||||
LISTEN_PORT=""
|
||||
PRIVATE_KEY=""
|
||||
ALLOWED_IPS=""
|
||||
ENDPOINTS=""
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
# Strip comments and leading/trailing whitespace
|
||||
line="${line%%#*}"
|
||||
line="${line# }"
|
||||
line="${line% }"
|
||||
[ -z "$line" ] && continue
|
||||
|
||||
case "$line" in
|
||||
\[*\] )
|
||||
_section="${line#\[}"
|
||||
_section="${_section%\]}"
|
||||
;;
|
||||
Address\ *=\ * )
|
||||
ADDRESS="${line#*= }"
|
||||
ADDRESS="${ADDRESS# }"
|
||||
;;
|
||||
DNS\ *=\ * )
|
||||
DNS="${line#*= }"
|
||||
DNS="${DNS# }"
|
||||
;;
|
||||
ListenPort\ *=\ * )
|
||||
LISTEN_PORT="${line#*= }"
|
||||
LISTEN_PORT="${LISTEN_PORT# }"
|
||||
;;
|
||||
PrivateKey\ *=\ * )
|
||||
PRIVATE_KEY="${line#*= }"
|
||||
PRIVATE_KEY="${PRIVATE_KEY# }"
|
||||
;;
|
||||
AllowedIPs\ *=\ * )
|
||||
_ips="${line#*= }"
|
||||
_ips="${_ips# }"
|
||||
if [ -z "$ALLOWED_IPS" ]; then
|
||||
ALLOWED_IPS="$_ips"
|
||||
else
|
||||
ALLOWED_IPS="$ALLOWED_IPS, $_ips"
|
||||
fi
|
||||
;;
|
||||
Endpoint\ *=\ * )
|
||||
_ep="${line#*= }"
|
||||
_ep="${_ep# }"
|
||||
_ep="${_ep%:*}" # strip port, keep only IP
|
||||
if [ -z "$ENDPOINTS" ]; then
|
||||
ENDPOINTS="$_ep"
|
||||
else
|
||||
ENDPOINTS="$ENDPOINTS $_ep"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < "$_cfg"
|
||||
}
|
||||
|
||||
# -- helpers -------------------------------------------------------------
|
||||
|
||||
is_iface_up() {
|
||||
ip link show "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
save_default_route() {
|
||||
# Returns the current IPv4 default route line, if any
|
||||
ip route show default 2>/dev/null || true
|
||||
}
|
||||
|
||||
add_routes() {
|
||||
# Add routes for AllowedIPs. Handles IPv4 only for BusyBox compat.
|
||||
# 0.0.0.0/0 is handled specially: it replaces the default route.
|
||||
_iface="$1"
|
||||
_ips="$2"
|
||||
_old_IFS="$IFS"
|
||||
|
||||
IFS=","
|
||||
for _net in $_ips; do
|
||||
_net="${_net# }"
|
||||
_net="${_net% }"
|
||||
[ -z "$_net" ] && continue
|
||||
|
||||
# Skip IPv6 - BusyBox ip may not support it
|
||||
case "$_net" in
|
||||
*:* ) continue ;;
|
||||
esac
|
||||
|
||||
if [ "$_net" = "0.0.0.0/0" ] || [ "$_net" = "0.0.0.0" ]; then
|
||||
ip route replace default dev "$_iface" 2>/dev/null || \
|
||||
ip route add default dev "$_iface" 2>/dev/null || true
|
||||
else
|
||||
ip route add "$_net" dev "$_iface" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
IFS="$_old_IFS"
|
||||
}
|
||||
|
||||
setup_dns() {
|
||||
_dns="$1"
|
||||
[ -z "$_dns" ] && return 0
|
||||
|
||||
cp /etc/resolv.conf "$DNS_BACKUP"
|
||||
|
||||
# Write minimal resolv.conf
|
||||
{
|
||||
echo "nameserver $_dns"
|
||||
} > /etc/resolv.conf
|
||||
}
|
||||
|
||||
restore_dns() {
|
||||
if [ -f "$DNS_BACKUP" ]; then
|
||||
cp "$DNS_BACKUP" /etc/resolv.conf
|
||||
rm -f "$DNS_BACKUP"
|
||||
fi
|
||||
}
|
||||
|
||||
# -- commands ------------------------------------------------------------
|
||||
|
||||
cmd_up() {
|
||||
_cfg="$(resolve_config "${1:-}")"
|
||||
|
||||
parse_config "$_cfg"
|
||||
|
||||
# Interface name comes from config filename (matching wg-quick behaviour)
|
||||
_iface_name="${_cfg##*/}" # strip path
|
||||
_iface_name="${_iface_name%.conf}" # strip .conf
|
||||
IFACE="${IFACE:-$_iface_name}"
|
||||
|
||||
STATE_FILE="/tmp/wg-connect.${IFACE}.state"
|
||||
|
||||
if is_iface_up "$IFACE"; then
|
||||
die "interface $IFACE already exists - run 'wg-connect down $IFACE' first"
|
||||
fi
|
||||
|
||||
# Check for port conflicts with any existing WireGuard interface
|
||||
if [ -n "$LISTEN_PORT" ]; then
|
||||
_used_ports="$(wg show interfaces 2>/dev/null | while read -r _wgif; do
|
||||
wg show "$_wgif" listen-port 2>/dev/null
|
||||
done)"
|
||||
for _p in $_used_ports; do
|
||||
[ "$_p" = "$LISTEN_PORT" ] && die "port $LISTEN_PORT is already in use by another WireGuard interface"
|
||||
done
|
||||
fi
|
||||
|
||||
# Trap to roll back partial setup on failure.
|
||||
_up_done=""
|
||||
trap 'rollback' INT TERM EXIT
|
||||
|
||||
rollback() {
|
||||
trap - INT TERM EXIT
|
||||
if [ -n "$_up_done" ]; then
|
||||
return 0
|
||||
fi
|
||||
# Undo whatever was created, in reverse order.
|
||||
restore_dns 2>/dev/null || true
|
||||
is_iface_up "$IFACE" && ip link del "$IFACE" 2>/dev/null || true
|
||||
for _ep in $ENDPOINT_ROUTES; do
|
||||
[ -z "$_ep" ] && continue
|
||||
ip route del "$_ep" 2>/dev/null || true
|
||||
done
|
||||
rm -f "$STATE_FILE"
|
||||
}
|
||||
|
||||
# Save current default route and extract its gateway
|
||||
DEFAULT_ROUTE="$(save_default_route)"
|
||||
_gw=""
|
||||
case "$DEFAULT_ROUTE" in
|
||||
*\ via\ * ) _gw="${DEFAULT_ROUTE##* via }"; _gw="${_gw%% *}" ;;
|
||||
esac
|
||||
|
||||
# Add explicit routes for WireGuard endpoints through the physical
|
||||
# gateway BEFORE we replace the default route, otherwise the encrypted
|
||||
# UDP packets have no path to the server.
|
||||
ENDPOINT_ROUTES=""
|
||||
for _ep in $ENDPOINTS; do
|
||||
[ -z "$_ep" ] && continue
|
||||
case "$_ep" in
|
||||
*:* ) continue ;; # skip IPv6 endpoints
|
||||
esac
|
||||
if [ -n "$_gw" ]; then
|
||||
ip route add "$_ep" via "$_gw" 2>/dev/null || true
|
||||
fi
|
||||
ENDPOINT_ROUTES="${ENDPOINT_ROUTES}${_ep} "
|
||||
done
|
||||
|
||||
# Create the interface
|
||||
ip link add "$IFACE" type wireguard
|
||||
|
||||
# Configure WireGuard - strip wg-quick-only fields (Address, DNS, etc.)
|
||||
# that wg setconf rejects. These are handled manually below.
|
||||
_wg_conf="/tmp/wg-connect.$$.conf"
|
||||
grep -v -E '^[[:space:]]*(Address|DNS|MTU|Table|PreUp|PostUp|PreDown|PostDown|SaveConfig)[[:space:]]*=' "$_cfg" > "$_wg_conf"
|
||||
wg setconf "$IFACE" "$_wg_conf"
|
||||
rm -f "$_wg_conf"
|
||||
|
||||
# Assign address (append /32 if no CIDR given)
|
||||
case "$ADDRESS" in
|
||||
*/* ) ;;
|
||||
* ) ADDRESS="${ADDRESS}/32" ;;
|
||||
esac
|
||||
ip addr add "$ADDRESS" dev "$IFACE"
|
||||
|
||||
# Bring interface up
|
||||
ip link set "$IFACE" up
|
||||
|
||||
# Add routes (including default route replacement)
|
||||
add_routes "$IFACE" "$ALLOWED_IPS"
|
||||
|
||||
# Set up DNS
|
||||
DNS_BACKUP="/tmp/resolv.conf.wg.bak"
|
||||
setup_dns "$DNS"
|
||||
|
||||
# Write state file for cmd_down
|
||||
{
|
||||
echo "IFACE=$IFACE"
|
||||
echo "DNS_BACKUP=$DNS_BACKUP"
|
||||
echo "DEFAULT_ROUTE=$DEFAULT_ROUTE"
|
||||
echo "ENDPOINT_ROUTES=$ENDPOINT_ROUTES"
|
||||
} > "$STATE_FILE"
|
||||
|
||||
# Mark success - disable rollback
|
||||
_up_done=1
|
||||
trap - INT TERM EXIT
|
||||
|
||||
echo "wg-connect: $IFACE is up"
|
||||
}
|
||||
|
||||
cmd_down() {
|
||||
# Resolve state file: explicit name, or default "peer" (from peer.conf)
|
||||
_down_name="${1:-}"
|
||||
|
||||
if [ -n "$_down_name" ]; then
|
||||
case "$_down_name" in
|
||||
*.conf ) _down_name="${_down_name%.conf}" ;;
|
||||
esac
|
||||
case "$_down_name" in
|
||||
*/* ) _down_name="${_down_name##*/}" ;;
|
||||
esac
|
||||
STATE_FILE="/tmp/wg-connect.${_down_name}.state"
|
||||
IFACE="$_down_name"
|
||||
fi
|
||||
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
# Read state
|
||||
while IFS='=' read -r key value; do
|
||||
case "$key" in
|
||||
IFACE) IFACE="$value" ;;
|
||||
DNS_BACKUP) DNS_BACKUP="$value" ;;
|
||||
DEFAULT_ROUTE) DEFAULT_ROUTE="$value" ;;
|
||||
ENDPOINT_ROUTES) ENDPOINT_ROUTES="$value" ;;
|
||||
esac
|
||||
done < "$STATE_FILE"
|
||||
|
||||
# Restore DNS
|
||||
restore_dns
|
||||
|
||||
# Remove endpoint routes
|
||||
for _ep in $ENDPOINT_ROUTES; do
|
||||
[ -z "$_ep" ] && continue
|
||||
ip route del "$_ep" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
# No state file - try to clean up a partially-created interface
|
||||
if [ -z "$IFACE" ]; then
|
||||
IFACE="$IFACE_DEFAULT"
|
||||
fi
|
||||
if ! is_iface_up "$IFACE"; then
|
||||
die "no state file found and $IFACE does not exist - nothing to tear down"
|
||||
fi
|
||||
echo "wg-connect: no state file, cleaning up leftover $IFACE" >&2
|
||||
fi
|
||||
|
||||
# Delete the interface (this also removes its routes and address)
|
||||
if is_iface_up "$IFACE"; then
|
||||
ip link del "$IFACE"
|
||||
fi
|
||||
|
||||
# Restore the previous default route if there was one
|
||||
if [ -n "$DEFAULT_ROUTE" ]; then
|
||||
ip route add $DEFAULT_ROUTE 2>/dev/null || true
|
||||
fi
|
||||
|
||||
rm -f "$STATE_FILE"
|
||||
|
||||
echo "wg-connect: $IFACE is down"
|
||||
}
|
||||
|
||||
# -- main ----------------------------------------------------------------
|
||||
|
||||
case "${1:-}" in
|
||||
up) shift; cmd_up "$@" ;;
|
||||
down) shift; cmd_down "$@" ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
.TH WG-CONNECT 1 "2026-06-23" "wg-connect 1.1" "User Commands"
|
||||
.SH NAME
|
||||
wg-connect \- bring a WireGuard tunnel up or down
|
||||
.SH SYNOPSIS
|
||||
.B wg-connect up
|
||||
.I config
|
||||
.br
|
||||
.B wg-connect down
|
||||
.RI [ name ]
|
||||
.SH DESCRIPTION
|
||||
.B wg-connect
|
||||
manages a WireGuard tunnel on systems that lack
|
||||
.BR wg-quick (8),
|
||||
systemd, or bash - typically BusyBox-based distributions.
|
||||
.PP
|
||||
The
|
||||
.B up
|
||||
command parses a WireGuard configuration file, creates the tunnel
|
||||
interface, applies keys and peers, assigns the address, installs
|
||||
routes, and optionally updates
|
||||
.IR /etc/resolv.conf .
|
||||
State is saved to
|
||||
.I /tmp/wg-connect.<iface>.state
|
||||
so that
|
||||
.B down
|
||||
can cleanly tear everything down.
|
||||
.PP
|
||||
The
|
||||
.B down
|
||||
command reverses every action: restores the original DNS configuration,
|
||||
removes endpoint-specific routes, deletes the interface, and restores
|
||||
the previous default route.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B up
|
||||
.I config
|
||||
Bring the tunnel up.
|
||||
.I config
|
||||
is required and is resolved as follows:
|
||||
.RS
|
||||
.IP \[bu]
|
||||
If it contains a
|
||||
.BR / ,
|
||||
it is used as a file path directly.
|
||||
.IP \[bu]
|
||||
If it ends in
|
||||
.BR .conf ,
|
||||
it is tried in the current directory, then in
|
||||
.IR /etc/wireguard/ .
|
||||
.IP \[bu]
|
||||
Otherwise it is treated as a name and looked up as
|
||||
.IR /etc/wireguard/ <name> .conf .
|
||||
.RE
|
||||
.TP
|
||||
.B down
|
||||
.RI [ name ]
|
||||
Tear a tunnel down. If
|
||||
.I name
|
||||
is given, the state file
|
||||
.I /tmp/wg-connect.<name>.state
|
||||
is used and the interface
|
||||
.I <name>
|
||||
is torn down. If omitted, the interface defaults to
|
||||
.BR wg0 .
|
||||
.RS
|
||||
.IP
|
||||
If the expected state file is missing but the interface still exists,
|
||||
it is cleaned up as a leftover from a failed
|
||||
.B up
|
||||
attempt.
|
||||
.RE
|
||||
.SH CONFIGURATION FORMAT
|
||||
The configuration file follows standard WireGuard syntax with an
|
||||
.B [Interface]
|
||||
section and one or more
|
||||
.B [Peer]
|
||||
sections. The
|
||||
.BR wg-quick (8)
|
||||
extensions
|
||||
.BR Address ,
|
||||
.BR DNS ,
|
||||
.BR MTU ,
|
||||
.BR Table ,
|
||||
.BR PreUp ,
|
||||
.BR PostUp ,
|
||||
.BR PreDown ,
|
||||
.BR PostDown ,
|
||||
and
|
||||
.B SaveConfig
|
||||
are tolerated in the file but are handled by
|
||||
.B wg-connect
|
||||
itself rather than passed to
|
||||
.BR "wg setconf" .
|
||||
.PP
|
||||
.BR Address ,
|
||||
.BR DNS ,
|
||||
.BR Endpoint ,
|
||||
and
|
||||
.B AllowedIPs
|
||||
are the fields that drive the script's own setup logic. All other
|
||||
fields are forwarded to the kernel through
|
||||
.BR "wg setconf" .
|
||||
.PP
|
||||
The interface name is derived from the configuration file's basename
|
||||
(minus the
|
||||
.I .conf
|
||||
extension), matching
|
||||
.BR wg-quick (8)
|
||||
behaviour.
|
||||
.PP
|
||||
Multiple
|
||||
.B [Peer]
|
||||
sections are supported.
|
||||
.B AllowedIPs
|
||||
values are accumulated across all peers, and an explicit route to each
|
||||
.BR Endpoint 's
|
||||
IP address is added through the original default gateway before the
|
||||
default route is replaced.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /etc/wireguard/*.conf
|
||||
Configuration files, looked up by name.
|
||||
.TP
|
||||
.I /tmp/wg-connect.<iface>.state
|
||||
Runtime state written by
|
||||
.B up
|
||||
and consumed by
|
||||
.BR down .
|
||||
.TP
|
||||
.I /tmp/resolv.conf.wg.bak
|
||||
Backup of
|
||||
.I /etc/resolv.conf
|
||||
taken before the VPN DNS is installed.
|
||||
.TP
|
||||
.I /tmp/wg-connect.<pid>.conf
|
||||
Temporary filtered configuration passed to
|
||||
.BR "wg setconf" .
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
0
|
||||
Success.
|
||||
.TP
|
||||
1
|
||||
An error occurred (missing arguments, configuration file not found,
|
||||
interface already up, state file missing on
|
||||
.BR down ,
|
||||
or usage error).
|
||||
.SH NOTES
|
||||
.B wg-connect
|
||||
must be run as root. It manipulates network interfaces, the routing
|
||||
table, and
|
||||
.IR /etc/resolv.conf .
|
||||
.PP
|
||||
IPv6 addresses in
|
||||
.B AllowedIPs
|
||||
and
|
||||
.B Endpoint
|
||||
are silently skipped. BusyBox
|
||||
.B ip
|
||||
often lacks reliable IPv6 support.
|
||||
.SH SEE ALSO
|
||||
.BR wg (8),
|
||||
.BR wg-quick (8),
|
||||
.BR ip (8)
|
||||
Reference in New Issue
Block a user