diff --git a/net/amnezia-tools/Makefile b/net/amnezia-tools/Makefile index ead229b31481..e08b9a55ec85 100644 --- a/net/amnezia-tools/Makefile +++ b/net/amnezia-tools/Makefile @@ -1,41 +1,43 @@ PORTNAME= amnezia DISTVERSIONPREFIX= v DISTVERSION= 1.0.20241018 -PORTREVISION= 2 +PORTREVISION= 3 CATEGORIES= net net-vpn PKGNAMESUFFIX= -tools MAINTAINER= vova@zote.me COMMENT= Fast, modern and secure VPN Tunnel with AmneziaVPN anti-detection WWW= https://github.com/amnezia-vpn/amneziawg-tools/ LICENSE= GPLv2 RUN_DEPENDS= bash:shells/bash USES= gmake shebangfix USE_GITHUB= yes GH_ACCOUNT= amnezia-vpn GH_PROJECT= amneziawg-tools USE_RC_SUBR= ${PORTNAME} SHEBANG_FILES= wg-quick/freebsd.bash -MAKE_ARGS+= DEBUG=no WITH_BASHCOMPLETION=yes WITH_SYSTEMDUNITS=no +MAKE_ARGS+= DEBUG=no \ + WITH_BASHCOMPLETION=yes \ + WITH_SYSTEMDUNITS=no MAKE_ENV+= MANDIR="${PREFIX}/share/man" \ SYSCONFDIR="${PREFIX}/etc" WRKSRC_SUBDIR= src post-patch: @${REINPLACE_CMD} -e 's|wg s|awg s|g; \ s|/usr/local/etc/wireguard|${ETCDIR}|' \ ${WRKSRC}/completion/wg-quick.bash-completion @${REINPLACE_CMD} -e 's|%%ETCDIR%%|${ETCDIR}|' \ ${WRKSRC}/wg-quick/freebsd.bash post-install: @${RMDIR} ${STAGEDIR}${ETCDIR}/amneziawg ${STRIP_CMD} ${STAGEDIR}${PREFIX}/bin/awg .include diff --git a/net/amnezia-tools/files/amnezia.in b/net/amnezia-tools/files/amnezia.in index 98010c013bdb..893d7e58677c 100644 --- a/net/amnezia-tools/files/amnezia.in +++ b/net/amnezia-tools/files/amnezia.in @@ -1,82 +1,82 @@ #!/bin/sh # PROVIDE: amnezia # REQUIRE: NETWORKING # KEYWORD: shutdown # # amnezia_enable (bool): Set to "YES" to enable amnezia. # (default: "NO") # # amnezia_interfaces (str): List of interfaces to bring up/down # on start/stop. (eg: "amn0 amn1") # (default: "") # amnezia_env (str): Environment variables for the userspace # implementation. (eg: "LOG_LEVEL=debug") # # amnezia_kmod (str): Kernel module to load. # (default: "if_amn", "" - no module) # # amnezia_confdirs (str): Directory to store the configuration files. # (default: "%%ETCDIR%%") . /etc/rc.subr name=amnezia rcvar=amnezia_enable extra_commands="reload status" start_cmd="${name}_start" stop_cmd="${name}_stop" reload_cmd="${name}_reload" status_cmd="${name}_status" amnezia_start() { kmod=${amnezia_kmod:-if_amn} ${amnezia_env:+eval export $amnezia_env} [ -n "${kmod}" ] && kldstat -q -n ${kmod} || kldload -n ${kmod} for interface in ${amnezia_interfaces}; do - %%PREFIX%%/bin/awg-quick up ${interface} + daemon %%PREFIX%%/bin/awg-quick up ${interface} done } amnezia_stop() { for interface in ${amnezia_interfaces}; do %%PREFIX%%/bin/awg-quick down ${interface} done } amnezia_reload() { ${amnezia_env:+eval export $amnezia_env} for interface in ${amnezia_interfaces}; do %%PREFIX%%/bin/awg-quick reload ${interface} done } amnezia_status() { ${amnezia_env:+eval export $amnezia_env} amnezia_status="0" for interface in ${amnezia_interfaces}; do %%PREFIX%%/bin/awg show ${interface} || amnezia_status="1" done return ${amnezia_status} } load_rc_config $name : ${amnezia_enable="NO"} : ${amnezia_interfaces=""} : ${amnezia_env=""} : ${amnezia_kmod="if_amn"} : ${amnezia_confdirs="%%ETCDIR%%"} ${amnezia_confdirs:+eval export AWG_QUICK_CONFIG_SEARCH_PATHS="$amnezia_confdirs"} run_rc_command "$1" diff --git a/net/amnezia-tools/files/patch-config.c b/net/amnezia-tools/files/patch-config.c index 4e07d978251a..68d939b8e675 100644 --- a/net/amnezia-tools/files/patch-config.c +++ b/net/amnezia-tools/files/patch-config.c @@ -1,50 +1,59 @@ --- config.c.orig 2024-10-01 13:02:42 UTC +++ config.c +@@ -252,7 +252,7 @@ static inline bool parse_endpoint(struct sockaddr *end + * + * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional. + */ +- if (ret == EAI_NONAME || ret == EAI_FAIL || ++ if (ret == EAI_FAIL || + #ifdef EAI_NODATA + ret == EAI_NODATA || + #endif @@ -337,6 +337,20 @@ static bool validate_netmask(struct wgallowedip *allow return true; } +static inline void parse_ip_prefix(struct wgpeer *peer, uint32_t *flags, char **mask) +{ + /* If the IP is prefixed with either '+' or '-' consider this an + * incremental change. Disable WGPEER_REPLACE_ALLOWEDIPS. */ + switch ((*mask)[0]) { + case '-': + *flags |= WGALLOWEDIP_REMOVE_ME; + /* fall through */ + case '+': + peer->flags &= ~WGPEER_REPLACE_ALLOWEDIPS; + ++(*mask); + } +} + static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value) { struct wgallowedip *allowedip = *last_allowedip, *new_allowedip; @@ -353,10 +367,18 @@ static inline bool parse_allowedips(struct wgpeer *pee } sep = mutable; while ((mask = strsep(&sep, ","))) { + uint32_t flags = 0; unsigned long cidr; char *end, *ip; + parse_ip_prefix(peer, &flags, &mask); + saved_entry = strdup(mask); + if (!saved_entry) { + perror("strdup"); + free(mutable); + return false; + } ip = strsep(&mask, "/"); new_allowedip = calloc(1, sizeof(*new_allowedip)); @@ -387,6 +409,7 @@ static inline bool parse_allowedips(struct wgpeer *pee else goto err; new_allowedip->cidr = cidr; + new_allowedip->flags = flags; if (!validate_netmask(new_allowedip)) fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask); diff --git a/net/amnezia-tools/files/patch-wg-quick_freebsd.bash b/net/amnezia-tools/files/patch-wg-quick_freebsd.bash index 6d218f256182..8ac17f331400 100644 --- a/net/amnezia-tools/files/patch-wg-quick_freebsd.bash +++ b/net/amnezia-tools/files/patch-wg-quick_freebsd.bash @@ -1,316 +1,337 @@ ---- wg-quick/freebsd.bash.orig 2024-10-01 13:02:42 UTC +--- wg-quick/freebsd.bash.orig 2025-10-19 18:21:50 UTC +++ wg-quick/freebsd.bash @@ -25,11 +25,20 @@ CONFIG_FILE="" POST_DOWN=( ) SAVE_CONFIG=0 CONFIG_FILE="" +DESCRIPTION="" +USERLAND=0 +MONITOR=1 +TRACK_DNS_CHANGES=0 PROGRAM="${0##*/}" ARGS=( "$@" ) IS_ASESCURITY_ON=0 + +declare -A ROUTES -+declare -A ENDPOINTS ++declare -A ENDPOINTS_MAP + + cmd() { echo "[#] $*" >&3 "$@" @@ -40,7 +49,7 @@ die() { exit 1 } -CONFIG_SEARCH_PATHS=( /etc/amnezia/amneziawg /usr/local/etc/amnezia/amneziawg ) +CONFIG_SEARCH_PATHS=( ${AWG_QUICK_CONFIG_SEARCH_PATHS:-%%ETCDIR%%} ) unset ORIGINAL_TMPDIR make_temp() { @@ -64,7 +73,7 @@ parse_options() { } parse_options() { - local interface_section=0 line key value stripped path v + local interface_section=0 line key value stripped path v last_public_key CONFIG_FILE="$1" if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then for path in "${CONFIG_SEARCH_PATHS[@]}"; do @@ -82,7 +91,7 @@ parse_options() { stripped="${line%%\#*}" key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}" value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}" - [[ $key == "["* ]] && interface_section=0 + [[ $key == "["* ]] && interface_section=0 && last_public_key="" [[ $key == "[Interface]" ]] && interface_section=1 if [[ $interface_section -eq 1 ]]; then case "$key" in @@ -96,9 +105,14 @@ parse_options() { PreDown) PRE_DOWN+=( "$value" ); continue ;; PostUp) POST_UP+=( "$value" ); continue ;; PostDown) POST_DOWN+=( "$value" ); continue ;; + Description) DESCRIPTION="$value"; continue ;; SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;; + UserLand) read_bool USERLAND "$value"; continue ;; + Monitor) read_bool MONITOR "$value"; continue ;; + TrackDNSChanges) TRACK_DNS_CHANGES="$value"; continue ;; esac case "$key" in + Jc);& Jmin);& Jmax);& @@ -109,6 +123,17 @@ parse_options() { H3);& H4) IS_ASESCURITY_ON=1;; esac + else + case "$key" in + PublicKey) last_public_key="$value" ;; + Routes) ROUTES["$last_public_key"]="$value"; continue ;; + Endpoint) + endpoint_host="${value%%:*}" + if ! [[ "$endpoint_host" =~ ^[0-9]+ ]]; then -+ ENDPOINTS["$last_public_key"]="$endpoint_host" ++ ENDPOINTS_MAP["$last_public_key"]="$endpoint_host" + fi + ;; + esac fi WG_CONFIG+="$line"$'\n' done < "$CONFIG_FILE" -@@ -129,12 +154,15 @@ add_if() { +@@ -129,19 +154,22 @@ add_if() { add_if() { local ret rc - local cmd="ifconfig wg create name "$INTERFACE"" - if [[ $IS_ASESCURITY_ON == 1 ]]; then + local cmd="ifconfig amn create name "$INTERFACE"" + if [[ $USERLAND == 1 ]]; then cmd="amneziawg-go "$INTERFACE""; fi - if ret="$(cmd $cmd 2>&1 >/dev/null)"; then - return 0 + if [ -n "$DESCRIPTION" ]; then + ret="$(cmd $cmd description "$DESCRIPTION" 2>&1 >/dev/null)" && return 0 + else + + ret="$(cmd $cmd 2>&1 >/dev/null)" && return 0 fi rc=$? if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then + echo "$ret" >&3 + return $rc + fi +- echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3 ++ echo "[!] Missing Amnezia kernel support ($ret). Falling back to slow userspace implementation." >&3 + cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-amneziawg-go}" "$INTERFACE" + } + @@ -209,7 +237,7 @@ set_mtu() { [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6 output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)" [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" - done < <(wg show "$INTERFACE" endpoints) + done < <(awg show "$INTERFACE" endpoints) if [[ $mtu -eq 0 ]]; then read -r output < <(route -n get default || true) || true [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}" @@ -242,7 +270,7 @@ collect_endpoints() { while read -r _ endpoint; do [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue ENDPOINTS+=( "${BASH_REMATCH[1]}" ) - done < <(wg show "$INTERFACE" endpoints) + done < <(awg show "$INTERFACE" endpoints) } set_endpoint_direct_route() { @@ -297,18 +325,18 @@ monitor_daemon() { } monitor_daemon() { + [[ $MONITOR -eq 0 ]] && return 0 echo "[+] Backgrounding route monitor" >&2 (make_temp trap 'del_routes; clean_temp; exit 0' INT TERM EXIT exec >/dev/null 2>&1 - exec 19< <(exec route -n monitor) + exec 19< <(exec stdbuf -oL route -n monitor) local event pid=$! # TODO: this should also check to see if the endpoint actually changes # in response to incoming packets, and then call set_endpoint_direct_route # then too. That function should be able to gracefully cleanup if the # endpoints change. while read -u 19 -r event; do - [[ $event == RTM_* ]] || continue ifconfig "$INTERFACE" >/dev/null 2>&1 || break [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route # TODO: set the mtu as well, but only if up -@@ -316,6 +344,77 @@ monitor_daemon() { +@@ -316,6 +344,76 @@ monitor_daemon() { kill $pid) & disown } +wg_endpoints() { + awk ' + BEGIN { RS=""; FS="\n" } + /Peer/ { + pk=""; ep="" + for (i = 1; i <= NF; i++) { + if ($i ~ /^PublicKey[ \t]*=/) { + pk = $i + sub(/^PublicKey[ \t]*=[ \t]*/, "", pk) + } + if ($i ~ /^Endpoint[ \t]*=/) { + ep = $i + sub(/^Endpoint[ \t]*=[ \t]*/, "", ep) + split(ep, parts, ":") + host = parts[1] + port = parts[2] + } + } + if (pk != "" && host != "" && port != "") { + print pk, host, port + } + } + ' +} + +tracker_pid_file() { + echo "/var/run/awg-quick.dns-tracker.${INTERFACE}.pid" +} + +monitor_dns_changes() { + local pk peer_ip port peer_host host_ip + [[ $TRACK_DNS_CHANGES -eq 0 ]] && return 0 + + echo "[+] Backgrounding DNS tracker" >&2 -+ exec >/dev/null 2>&1 + + pid_file="$(tracker_pid_file)" + [[ -f "$pid_file" ]] && kill $(cat "$pid_file") 2>/dev/null || true + + ( + trap 'rm -f "$pid_file"; exit 0' INT TERM EXIT + + set -e + while true; do + sleep $TRACK_DNS_CHANGES & + wait $! + + $cmd awg showconf "$INTERFACE" 2> /dev/null | wg_endpoints | \ + while read -r pk peer_ip port; do -+ peer_host="${ENDPOINTS[$pk]}" ++ peer_host="${ENDPOINTS_MAP[$pk]}" + if [[ -n "$peer_host" ]]; then + host_ip=$(host "$peer_host" 2>/dev/null | awk '/has address/ { print $4; exit; }') || continue + + if [[ "$host_ip" = "$peer_ip" ]]; then + #echo "$pk matches ${peer_ip} <=> ${host_ip}" + : + else + logger -t awg-quick -p local0.notice \ + "$INTERFACE/$pk host $peer_host:" \ + "IP missmatch: $host_ip != $peer_ip, configuring endpoint" || true + $cmd awg set "$INTERFACE" peer "$pk" endpoint "$peer_host:$port" || true + fi + fi + done + + done -+ ) & disown ++ ) /dev/null 2>&1 3>&- & disown + echo "$!" > "$pid_file" +} + + HAVE_SET_DNS=0 set_dns() { [[ ${#DNS[@]} -gt 0 ]] || return 0 -@@ -354,7 +453,7 @@ set_config() { +@@ -354,7 +452,7 @@ set_config() { } set_config() { - echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin + echo "$WG_CONFIG" | cmd awg setconf "$INTERFACE" /dev/stdin } save_config() { -@@ -386,7 +485,7 @@ save_config() { +@@ -386,7 +484,7 @@ save_config() { done old_umask="$(umask)" umask 077 - current_config="$(cmd wg showconf "$INTERFACE")" + current_config="$(cmd awg showconf "$INTERFACE")" trap 'rm -f "$CONFIG_FILE.tmp"; clean_temp; exit' INT TERM EXIT echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file" sync "$CONFIG_FILE.tmp" -@@ -433,6 +532,20 @@ cmd_usage() { +@@ -412,7 +510,7 @@ cmd_usage() { + followed by \`.conf'. Otherwise, INTERFACE is an interface name, with + configuration found at: + ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}. +- It is to be readable by wg(8)'s \`setconf' sub-command, with the exception ++ It is to be readable by awg(8)'s \`setconf' sub-command, with the exception + of the following additions to the [Interface] section, which are handled + by $PROGRAM: + +@@ -429,10 +527,24 @@ cmd_usage() { + - SaveConfig: if set to \`true', the configuration is saved from the current + state of the interface upon shutdown. + +- See wg-quick(8) for more info and examples. ++ See awg-quick(8) for more info and examples. _EOF } +get_routes() { + while read -r pub_key i; do + if [[ -v "ROUTES[$pub_key]" ]]; then + for route in ${ROUTES[$pub_key]//,/ }; do + echo "$route" + done + else + for j in $i; do + [[ $j =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$j" + done + fi + done < <(awg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t / +} + cmd_up() { local i [[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists" -@@ -446,26 +559,31 @@ cmd_up() { +@@ -446,26 +558,31 @@ cmd_up() { set_mtu up_if set_dns - for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do + for i in $(get_routes); do add_route "$i" done [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route monitor_daemon + monitor_dns_changes execute_hooks "${POST_UP[@]}" trap 'clean_temp; exit' INT TERM EXIT } cmd_down() { - [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" -+ [[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" ++ [[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a Amnezia interface" execute_hooks "${PRE_DOWN[@]}" [[ $SAVE_CONFIG -eq 0 ]] || save_config del_if unset_dns + if [[ -f "$(tracker_pid_file)" ]]; then + kill $(cat "$(tracker_pid_file)") 2>/dev/null + rm -f "$(tracker_pid_file)" + fi execute_hooks "${POST_DOWN[@]}" } cmd_save() { - [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" -+ [[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface" ++ [[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a Amnezia interface" save_config } -@@ -473,6 +591,10 @@ cmd_strip() { +@@ -473,6 +590,10 @@ cmd_strip() { echo "$WG_CONFIG" } +cmd_reload() { + cmd awg setconf "$INTERFACE" <(cmd_strip) +} + # ~~ function override insertion point ~~ make_temp -@@ -496,6 +618,10 @@ elif [[ $# -eq 2 && $1 == strip ]]; then +@@ -496,6 +617,10 @@ elif [[ $# -eq 2 && $1 == strip ]]; then auto_su parse_options "$2" cmd_strip +elif [[ $# -eq 2 && $1 == reload ]]; then + auto_su + parse_options "$2" + cmd_reload else cmd_usage exit 1