mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 04:53:00 +08:00
⛅ Sync 2026-10-11 13:16:00
This commit is contained in:
1 parent
705dadd362
commit
e6d1b4e2d7
105 files changed
+26511
-50
No files matched your search
+73
@@ -0,0 +1,73 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2008-2026 The OpenWrt Project
|
||||
# Copyright (C) 2026 Manfred Jaider <info@masmbit.com>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# luci-app-openvpn-plus : Network-ready bootup event handler with race-condition lock
|
||||
# /etc/hotplug.d/iface/35-luci-app-openvpn-plus-bootup
|
||||
|
||||
# Only trigger this script when an interface finishes initializing
|
||||
if [ "$ACTION" != "ifup" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# STRIP LOOPBACK: Ignore the internal system loopback to prevent a premature trigger
|
||||
if [ "$INTERFACE" = "loopback" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
LOCK_FILE="/var/run/luci-app-openvpn-plus-bootup.lock"
|
||||
|
||||
# Mutex implementation preventing parallel execution threads from racing each other
|
||||
if [ -f "$LOCK_FILE" ]; then
|
||||
# Clean verification via signal 0: check if the holding PID is still alive
|
||||
if kill -0 $(cat "$LOCK_FILE" 2>/dev/null) 2>/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Register active PID into the lock file to serialize the bootup checks
|
||||
echo "$$" > "$LOCK_FILE"
|
||||
|
||||
# Set traps to delete the lock file reliably on unexpected script exits
|
||||
trap "rm -f $LOCK_FILE" EXIT INT TERM
|
||||
|
||||
logger -t openvpn-luci "Bootup: Interface '${INTERFACE}' is up. Checking active OpenVPN profile running state in 10 seconds..."
|
||||
|
||||
# Give the system firewall and routing tables enough time to init
|
||||
sleep 10
|
||||
|
||||
# Loop through all configured openvpn sections inside the UCI configuration
|
||||
uci -q show openvpn | grep "=openvpn" | while read -r line; do
|
||||
# Extract the pure instance identity string from the current UCI line
|
||||
inst="${line##*.}"
|
||||
inst="${inst%%=*}"
|
||||
|
||||
# Process the instance only if it is explicitly enabled by the user
|
||||
if [ "$(uci -q get openvpn.${inst}.enabled)" = "1" ]; then
|
||||
# Bulletproof process check utilizing the immune syslog tag pattern
|
||||
if ! ps | grep -v "grep" | grep -q "openvpn(${inst})"; then
|
||||
logger -t openvpn-luci "Bootup: Detected unstarted instance '${inst}'. Initializing process."
|
||||
|
||||
# Restart the service cleanly to clear out any dead zombie states or locked ports
|
||||
/etc/init.d/openvpn stop "$inst" >/dev/null 2>&1
|
||||
sleep 1 >/dev/null 2>&1
|
||||
/etc/init.d/openvpn restart "$inst" >/dev/null 2>&1
|
||||
sleep 1 >/dev/null 2>&1
|
||||
/etc/init.d/openvpn reload "$inst" >/dev/null 2>&1
|
||||
else
|
||||
logger -t openvpn-luci "Bootup: Instance '${inst}' is already running safely."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete the lock file manually after successful execution
|
||||
rm -f "$LOCK_FILE"
|
||||
|
||||
# Unregister and reset all traps to system default values before exiting
|
||||
trap - EXIT INT TERM
|
||||
|
||||
exit 0
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2008-2026 The OpenWrt Project
|
||||
# Copyright (C) 2026 Manfred Jaider <info@masmbit.com>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# luci-app-openvpn-plus : OpenWrt hotplug lifecycle manager for ddns server and client common name
|
||||
# /etc/hotplug.d/openvpn/35-luci-app-openvpn-plus-lifecycle
|
||||
|
||||
# Ensure we catch the active OpenVPN name context across all OpenWrt variants
|
||||
ovpn_instance="${INSTANCE:-$NAME}"
|
||||
|
||||
# Fallback to $1 if $dev is empty depending on the environment context
|
||||
ovpn_dev="${dev:-$1}"
|
||||
|
||||
if [ -z "$ovpn_instance" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
CONF_FILE="/etc/openvpn/luci/${ovpn_instance}.conf"
|
||||
PID_FILE="/var/run/openvpn-ddns-${ovpn_instance}.pid"
|
||||
|
||||
case "$ACTION" in
|
||||
|
||||
up)
|
||||
|
||||
inst_role=$(uci -q get openvpn."$ovpn_instance".role)
|
||||
|
||||
# Save the active interface name into the OpenWrt UCI configuration
|
||||
if [ "$inst_role" = "server" ] || [ "$inst_role" = "client" ]; then
|
||||
if [ -n "$ovpn_dev" ]; then
|
||||
current_devname=$(uci -q get openvpn."$ovpn_instance".devname)
|
||||
if [ "$current_devname" != "$ovpn_dev" ]; then
|
||||
uci -q set openvpn."$ovpn_instance".devname="$ovpn_dev"
|
||||
uci commit openvpn
|
||||
logger -t openvpn-luci "Hotplug (${ovpn_instance}): set devname ${ovpn_dev}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# SERVER: DDNS SERVICE START
|
||||
if [ "$inst_role" = "server" ]; then
|
||||
|
||||
# Stop any old DDNS service processes if they are still running
|
||||
if start-stop-daemon -K -q -p "$PID_FILE" -t 2>/dev/null; then
|
||||
logger -t openvpn-luci "Hotplug: Stopping active DDNS service process for ${ovpn_instance} before configuration reload."
|
||||
start-stop-daemon -K -q -p "$PID_FILE" 2>/dev/null
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
|
||||
# Process the new configuration only if the file exists
|
||||
if [ -f "$CONF_FILE" ]; then
|
||||
domain=$(grep -E "^setenv[[:space:]]+DDNS[[:space:]]+" "$CONF_FILE" | sed -E 's/^setenv[[:space:]]+DDNS[[:space:]]+"(.*)"/\1/')
|
||||
url=$(grep -E "^setenv[[:space:]]+DDNS_URL[[:space:]]+" "$CONF_FILE" | sed -E 's/^setenv[[:space:]]+DDNS_URL[[:space:]]+"(.*)"/\1/')
|
||||
|
||||
if [ -n "$domain" ] && [ -n "$url" ]; then
|
||||
logger -t openvpn-luci "Hotplug: Starting DDNS background service for ${ovpn_instance} (Domain: ${domain})"
|
||||
start-stop-daemon -S -b -m -p "$PID_FILE" -x /usr/libexec/luci-app-openvpn-plus -- ddnsservice "$domain" "$url" "$ovpn_instance"
|
||||
fi
|
||||
fi
|
||||
# CLIENT: EXTRACT COMMON NAME
|
||||
elif [ "$inst_role" = "client" ]; then
|
||||
cn_update="0"
|
||||
# Extract the last connection initiation line from logread for this specific instance
|
||||
log_line=$(logread | grep "openvpn(${ovpn_instance})" | grep "Peer Connection Initiated" | tail -n 1)
|
||||
|
||||
if [ -n "$log_line" ]; then
|
||||
# Parse the pure CN string from inside the brackets (Example: [OfficeServer] -> OfficeServer)
|
||||
server_cn=$(echo "$log_line" | sed -n 's/.*: \[\([^]]*\)\].*Peer Connection Initiated.*/\1/p')
|
||||
|
||||
if [ -n "$server_cn" ]; then
|
||||
# Write the resolved Common Name safely into the OpenWrt UCI configuration
|
||||
uci -q set openvpn."$ovpn_instance".server_common_name="$server_cn"
|
||||
uci commit openvpn
|
||||
cn_update="1"
|
||||
fi
|
||||
fi
|
||||
if [ "$cn_update" = "1" ]; then
|
||||
logger -t openvpn-luci "Hotplug (${ovpn_instance}): Server Common Name '${server_cn}' resolved"
|
||||
else
|
||||
logger -t openvpn-luci "Hotplug (${ovpn_instance}): Server Common Name not found in Peer Connection log entry"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
down)
|
||||
|
||||
inst_role=$(uci -q get openvpn."$ovpn_instance".role)
|
||||
|
||||
# Clear the mapped network device because the tunnel is offline
|
||||
if [ "$inst_role" = "server" ] || [ "$inst_role" = "client" ]; then
|
||||
uci -q delete openvpn."$ovpn_instance".devname
|
||||
uci commit openvpn
|
||||
fi
|
||||
|
||||
# SERVER: DDNS SERVICE STOP
|
||||
if [ "$inst_role" = "server" ]; then
|
||||
|
||||
# Clean application stop via standard system manager signals
|
||||
if start-stop-daemon -K -q -p "$PID_FILE" -t 2>/dev/null; then
|
||||
logger -t openvpn-luci "Hotplug: Stopping DDNS service for ${ovpn_instance} because tunnel is down."
|
||||
start-stop-daemon -K -q -p "$PID_FILE" 2>/dev/null
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,37 @@
|
||||
# Modern OpenVPN Client Configuration Instance
|
||||
|
||||
# --- Connection Mode ---
|
||||
client
|
||||
dev tun
|
||||
proto udp
|
||||
|
||||
# --- Network & IP Management ---
|
||||
remote 127.0.0.1 1194
|
||||
float
|
||||
|
||||
# --- Tuning & Optimization ---
|
||||
explicit-exit-notify 1
|
||||
mssfix 1360
|
||||
tun-mtu 1420
|
||||
|
||||
# --- Certificates & Keys ---
|
||||
ca /etc/openvpn/keys/ca_default.crt
|
||||
cert /etc/openvpn/keys/client_default.crt
|
||||
key /etc/openvpn/keys/client_default.key
|
||||
|
||||
# --- Hardening & Security ---
|
||||
tls-crypt-v2 /etc/openvpn/keys/client_default.tls2.key
|
||||
tls-groups secp256r1
|
||||
cipher AES-256-GCM
|
||||
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
|
||||
|
||||
# --- Verification & Logging ---
|
||||
status /tmp/run/openvpn.instance.status 5
|
||||
status-version 1
|
||||
setenv logfilter on
|
||||
connect-retry 10 60
|
||||
resolv-retry infinite
|
||||
nobind
|
||||
persist-tun
|
||||
remote-cert-tls server
|
||||
verb 3
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2008-2026 The OpenWrt Project
|
||||
# Copyright (C) 2026 Manfred Jaider <info@masmbit.com>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# luci-app-openvpn : smart firewall rules
|
||||
|
||||
# USEFUL COMMANDS FOR FIREWALL MANAGEMENT & LIVE MONITORING
|
||||
#
|
||||
# * Core Firewall Controls
|
||||
# - Restart entire firewall framework: /etc/init.d/firewall restart
|
||||
# - Reload UCI configuration profiles: /etc/init.d/firewall reload
|
||||
# - View permanent firewall settings: cat /etc/config/firewall
|
||||
#
|
||||
# * Monitoring Chains & Structure
|
||||
# - View entire dynamic fw4 table: nft -a list table inet fw4
|
||||
# - View main input chain with handles: nft -a list chain inet fw4 input
|
||||
# - View main forward chain with handles: nft -a list chain inet fw4 forward
|
||||
# - View your custom OpenVPN sub-chain: nft -a list chain inet fw4 ovpn_filter_instance0
|
||||
# - Quick view of custom sub-chain: nft -a list chain inet fw4 ovpn_filter_instance0 | sed -E -n 's/.*(openvpn[^"]+).*counter (packets [0-9]+ bytes [0-9]+).*(# handle [0-9]+).*/\3 -> "\1" counter \2/p'
|
||||
#
|
||||
# * Live Security & Attacker Tracking (The most useful commands)
|
||||
# - View active IPv4 blocked IPs & timers: nft list set inet fw4 ovpn_block_ipv4_instance0
|
||||
# - Flush/Clear a specific ban list now: nft flush set inet fw4 ovpn_block_ipv4_instance0
|
||||
|
||||
fwPort=443
|
||||
fwProto="tcp"
|
||||
SUB_CHAIN="ovpn_filter_instance0"
|
||||
|
||||
# STEP 1: CLEAN UP OLD RULES AND SETS (Prevents duplicates and memory leaks)
|
||||
|
||||
# Find and delete the old jump rule handle inside the main input chain to unlock the sub-chain
|
||||
oldJumpHandle=$(nft -a list chain inet fw4 input | grep "jump $SUB_CHAIN" | grep -oE 'handle [0-9]+' | tail -n 1 | awk '{print $2}')
|
||||
if [ -n "$oldJumpHandle" ]; then
|
||||
nft "delete rule inet fw4 input handle $oldJumpHandle"
|
||||
fi
|
||||
|
||||
# Now the sub-chain is unlocked and can be safely deleted without resource busy errors
|
||||
nft "flush chain inet fw4 $SUB_CHAIN" 2>/dev/null
|
||||
nft "delete chain inet fw4 $SUB_CHAIN" 2>/dev/null
|
||||
nft "flush set inet fw4 ovpn_block_ipv4_instance0" 2>/dev/null
|
||||
nft "delete set inet fw4 ovpn_block_ipv4_instance0" 2>/dev/null
|
||||
nft "flush set inet fw4 ovpn_block_ipv6_instance0" 2>/dev/null
|
||||
nft "delete set inet fw4 ovpn_block_ipv6_instance0" 2>/dev/null
|
||||
|
||||
# STEP 2: BUILD FRESH NAMED SETS WITH AUTOMATIC TIMEOUT
|
||||
nft "add set inet fw4 ovpn_block_ipv4_instance0 { type ipv4_addr ; flags timeout ; }"
|
||||
nft "add set inet fw4 ovpn_block_ipv6_instance0 { type ipv6_addr ; flags timeout ; }"
|
||||
|
||||
# STEP 3: CREATE THE CLEAN SUB-CHAIN CONTAINER
|
||||
nft "add chain inet fw4 $SUB_CHAIN"
|
||||
|
||||
# STEP 4: BUILD THE CASCADE RULES (based on protocol)
|
||||
if [ "$fwProto" = "tcp" ]; then
|
||||
|
||||
# TCP-S1: Instantly rejects traffic from IPv4/IPv6 addresses listed in the block sets.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 ip saddr @ovpn_block_ipv4_instance0 log prefix \"openvpn(instance0)[firewall]{ban}: \" counter reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 ip6 saddr @ovpn_block_ipv6_instance0 log prefix \"openvpn(instance0)[firewall]{ban}: \" counter reject with tcp reset"
|
||||
|
||||
# TCP-S2: Blocks fragmented TCP packets before they can cause buffer overflows.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 ip frag-off & 0x3fff != 0 log prefix \"openvpn(instance0)[firewall]{fragment}: \" counter reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 exthdr frag exists log prefix \"openvpn(instance0)[firewall]{fragment}: \" counter reject with tcp reset"
|
||||
|
||||
# TCP-S3: Validates strict TCP headers and forces standard handshake behaviors.
|
||||
# - TCP Timestamps is required (This forces the TCP header size to be exactly 32 bytes for every data packets)
|
||||
# - SYN packets must have maxseg, window and sack-perm option.
|
||||
# - Data packets are forbidden to have the maxseg option.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp option timestamp missing log prefix \"openvpn(instance0)[firewall]{timestamp}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == syn tcp option maxseg missing log prefix \"openvpn(instance0)[firewall]{maxseg}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == 0 tcp option maxseg exists log prefix \"openvpn(instance0)[firewall]{maxseg}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == syn tcp option window missing log prefix \"openvpn(instance0)[firewall]{wscale}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == syn tcp option sack-perm missing log prefix \"openvpn(instance0)[firewall]{sack}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp option timestamp missing log prefix \"openvpn(instance0)[firewall]{timestamp}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == syn tcp option maxseg missing log prefix \"openvpn(instance0)[firewall]{maxseg}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == 0 tcp option maxseg exists log prefix \"openvpn(instance0)[firewall]{maxseg}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == syn tcp option window missing log prefix \"openvpn(instance0)[firewall]{wscale}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == syn tcp option sack-perm missing log prefix \"openvpn(instance0)[firewall]{sack}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
|
||||
# TCP-S4: Catches smart bots that delay data transmission to bypass early scanners.
|
||||
# * CRITICAL MINIMUM DATA SIZES (Worst-Case Real OpenVPN Client):
|
||||
# - Smallest possible tls-crypt-v2 key payload from a real client: 264 Bytes.
|
||||
# - IPv4: Handshake (Packets 1-3) + Key (Packet 4) = 488 Bytes minimum.
|
||||
# If network reception is very bad up to Packet 10 (empty ACKs): 748 Bytes total min.
|
||||
# - IPv6: Handshake (Packets 1-3) + Key (Packet 4) = 548 Bytes minimum.
|
||||
# If network reception is very bad up to Packet 10 (empty ACKs): 928 Bytes total min.
|
||||
# * SAFE LOCKOUT THRESHOLDS (How the firewall decides):
|
||||
# - IPv4 limit is set to < 700 Bytes. Bots with no data are smaller and get killed.
|
||||
# - IPv6 limit is set to < 900 Bytes. Bots with no data are smaller and get killed.
|
||||
# - Real worst-case OpenVPN users always stay safely above this byte radar.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 ct packets > 9 meta nfproto ipv4 ct bytes < 700 log prefix \"openvpn(instance0)[firewall]{stealth}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 ct packets > 9 meta nfproto ipv6 ct bytes < 900 log prefix \"openvpn(instance0)[firewall]{stealth}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
|
||||
# TCP-S5: Blocks payload data hidden inside the very first SYN packet (80 bytes for IPv4 packets and max 100 bytes for IPv6)
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == syn tcp option maxseg exists meta length > 80 log prefix \"openvpn(instance0)[firewall]{fastopen}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == syn tcp option maxseg exists meta length > 100 log prefix \"openvpn(instance0)[firewall]{fastopen}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
|
||||
# TCP-S6: Deep Packet Inspection for bad protocol lengths and fake small sizes.
|
||||
# - Starts only when a data packet is bigger than 53 bytes (IPv4) or 73 bytes (IPv6).
|
||||
# - Reads the 16-bit OpenVPN packet length field at the exact bit offset pointer 256.
|
||||
# - Catches bad opcodes, web text (like GET/POST), and fake sizes (payload < 155, payload > 1499).
|
||||
# - Real users with tls-crypt-v2 are safe because encrypted traffic never matches this bad matrix.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == 0 meta length > 53 @th,256,16 < 0x009b log prefix \"openvpn(instance0)[firewall]{payload}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv4 tcp flags & (syn) == 0 meta length > 53 @th,256,16 > 0x05db log prefix \"openvpn(instance0)[firewall]{payload}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == 0 meta length > 73 @th,256,16 < 0x009b log prefix \"openvpn(instance0)[firewall]{payload}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort ct packets < 12 meta nfproto ipv6 tcp flags & (syn) == 0 meta length > 73 @th,256,16 > 0x05db log prefix \"openvpn(instance0)[firewall]{payload}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } reject with tcp reset"
|
||||
|
||||
# TCP-S7: Packets that survived all previous checks are genuine.
|
||||
nft "add rule inet fw4 $SUB_CHAIN tcp dport $fwPort accept"
|
||||
|
||||
else
|
||||
|
||||
# UDP-S1: Instantly drops traffic from IPv4/IPv6 addresses listed in the block sets.
|
||||
# (Bleibt bei ct state new, damit bestehender established Tunnel-Traffic niemals blockiert wird!)
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new ip saddr @ovpn_block_ipv4_instance0 log prefix \"openvpn(instance0)[firewall]{ban}: \" counter drop"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new ip6 saddr @ovpn_block_ipv6_instance0 log prefix \"openvpn(instance0)[firewall]{ban}: \" counter drop"
|
||||
|
||||
# UDP-S2: Blocks fragmented UDP packets before they can cause buffer overflows.
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 ip frag-off & 0x3fff != 0 log prefix \"openvpn(instance0)[firewall]{fragment}: \" counter drop"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv6 exthdr frag exists log prefix \"openvpn(instance0)[firewall]{fragment}: \" counter drop"
|
||||
|
||||
# UDP-S3: Smallest valid OpenVPN packet is 48 bytes. Anything smaller is dropped.
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new udp length < 48 log prefix \"openvpn(instance0)[firewall]{short}: \" counter drop"
|
||||
|
||||
# UDP-S4: Drops and bans new IPv4 bots that scan with a zero checksum to save time.
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 udp checksum 0 log prefix \"openvpn(instance0)[firewall]{chksum}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } drop"
|
||||
|
||||
# UDP-S5: Whitelist valid initial OpenVPN opcodes P_CONTROL_HARD_RESET_CLIENT_V1,2,3 (skipping the 64-bit UDP header)
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 @th,64,8 & 0xf8 == 0x08 udp length > 250 accept"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 @th,64,8 & 0xf8 == 0x38 udp length > 250 accept"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 @th,64,8 & 0xf8 == 0x50 udp length > 250 accept"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv6 @th,64,8 & 0xf8 == 0x08 udp length > 250 accept"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv6 @th,64,8 & 0xf8 == 0x38 udp length > 250 accept"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv6 @th,64,8 & 0xf8 == 0x50 udp length > 250 accept"
|
||||
|
||||
# UDP-S6: Instantly ban and drop any other packet that tries to start a new connection
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv4 log prefix \"openvpn(instance0)[firewall]{opcode}: \" counter update @ovpn_block_ipv4_instance0 { ip saddr timeout 5m } drop"
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort ct state new meta nfproto ipv6 log prefix \"openvpn(instance0)[firewall]{opcode}: \" counter update @ovpn_block_ipv6_instance0 { ip6 saddr timeout 5m } drop"
|
||||
|
||||
# UDP-S7: Packets that survived all previous checks are genuine.
|
||||
nft "add rule inet fw4 $SUB_CHAIN udp dport $fwPort accept"
|
||||
|
||||
fi
|
||||
|
||||
# STEP 4: LINK THE SUB-CHAIN TO THE MAIN INPUT (single jump rule)
|
||||
|
||||
# Find the starting anchor in the main input chain (Loopback handle)
|
||||
mainAnchorHandle=$(nft -a list chain inet fw4 input | grep -E 'iif ["'\'']?lo["'\'']? accept' | grep -oE 'handle [0-9]+' | tail -n 1 | awk '{print $2}')
|
||||
|
||||
if [ -z "$mainAnchorHandle" ]; then
|
||||
mainAnchorHandle=35
|
||||
fi
|
||||
|
||||
# Inject the dynamic jump trigger right below the loopback anchor rule
|
||||
nft "add rule inet fw4 input handle $mainAnchorHandle $fwProto dport $fwPort jump $SUB_CHAIN"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# Modern OpenVPN Server Configuration Instance
|
||||
|
||||
# --- Connection Mode ---
|
||||
dev tun
|
||||
proto udp
|
||||
|
||||
# --- Network & IP Management ---
|
||||
port 1194
|
||||
setenv portextern 1194
|
||||
server 10.8.0.0 255.255.255.0
|
||||
server-ipv6 fd00:db8:0:1::/64
|
||||
topology subnet
|
||||
|
||||
# --- Tuning & Optimization ---
|
||||
tls-timeout 5
|
||||
mssfix 1360
|
||||
tun-mtu 1420
|
||||
# Increased max-clients absorbs attacks and client roaming
|
||||
max-clients 4096
|
||||
|
||||
# --- Certificates & Keys ---
|
||||
ca /etc/openvpn/keys/ca_default.crt
|
||||
cert /etc/openvpn/keys/server_default.crt
|
||||
key /etc/openvpn/keys/server_default.key
|
||||
dh /etc/openvpn/keys/dh_default.pem
|
||||
|
||||
# --- Hardening & Security ---
|
||||
tls-crypt-v2 /etc/openvpn/keys/server_default.tls2.key
|
||||
tls-groups secp256r1
|
||||
cipher AES-256-GCM
|
||||
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
|
||||
|
||||
# --- Smart Firewall ---
|
||||
setenv ovpnfirewall on
|
||||
|
||||
# --- Client Push Options ---
|
||||
push "dhcp-option DNS 9.9.9.9"
|
||||
push "dhcp-option DNS 149.112.112.112"
|
||||
push "dhcp-option DNS6 2620:fe::fe"
|
||||
push "dhcp-option DNS6 2620:fe::9"
|
||||
|
||||
# --- Verification & Logging ---
|
||||
status /tmp/run/openvpn.instance.status 5
|
||||
status-version 1
|
||||
setenv logfilter on
|
||||
keepalive 10 60
|
||||
verb 3
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2008-2026 The OpenWrt Project
|
||||
# Copyright (C) 2026 Manfred Jaider <info@masmbit.com>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# luci-app-openvpn-plus : clean default sample profiles and prepare empty configuration
|
||||
# /etc/uci-defaults/35_luci-app-openvpn-plus
|
||||
|
||||
# Path to the UCI configuration file
|
||||
CONFIG_FILE="${IPKG_INSTROOT}/etc/config/openvpn"
|
||||
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
# 1. Verify that all three standard firmware sample entries exist exactly as delivered
|
||||
if grep -q "config openvpn custom_config" "$CONFIG_FILE" 2>/dev/null && \
|
||||
grep -q "config openvpn sample_server" "$CONFIG_FILE" 2>/dev/null && \
|
||||
grep -q "config openvpn sample_client" "$CONFIG_FILE" 2>/dev/null; then
|
||||
|
||||
# 2. Check specifically for the UCI section pattern created by LuCI
|
||||
if ! grep -q "config openvpn 'instance" "$CONFIG_FILE" 2>/dev/null; then
|
||||
logger -t luci-app-openvpn-plus "Unmodified factory samples detected. Backing up to openvpn-def."
|
||||
|
||||
# Backup the cluttered firmware default file safely
|
||||
cp -f "$CONFIG_FILE" "${IPKG_INSTROOT}/etc/config/openvpn-def" 2>/dev/null
|
||||
|
||||
# Create a clean, empty base configuration for your LuCI app
|
||||
echo "# Clean OpenVPN base configuration" > "$CONFIG_FILE"
|
||||
|
||||
CLEAN_OPENVPN_SERVICE=1
|
||||
|
||||
fi
|
||||
else
|
||||
# If any check fails, the user has modified the file. DO NOT TOUCH IT!
|
||||
logger -t luci-app-openvpn-plus "Modified or custom OpenVPN configuration detected. Keeping user data safe."
|
||||
fi
|
||||
|
||||
# 3. Check if we are running on a live runtime system (opkg install / apk add)
|
||||
if [ -z "${IPKG_INSTROOT}" ]; then
|
||||
logger -t luci-app-openvpn-plus "Package installed at runtime. Clearing all cache and reloading services."
|
||||
|
||||
# Restart rpcd in the background after 3 seconds! This gives LuCI enough time to finish the installation in the browser.
|
||||
(
|
||||
# Restart the openvpn service only if we cleaned the config above
|
||||
if [ "$CLEAN_OPENVPN_SERVICE" = "1" ] && [ -x "/etc/init.d/openvpn" ]; then
|
||||
/etc/init.d/openvpn stop >/dev/null 2>&1
|
||||
sleep 1
|
||||
/etc/init.d/openvpn restart >/dev/null 2>&1
|
||||
sleep 1
|
||||
/etc/init.d/openvpn reload >/dev/null 2>&1
|
||||
sleep 1
|
||||
else
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Delete ALL temporary LuCI files.
|
||||
rm -rf /tmp/luci-* >/dev/null 2>&1
|
||||
|
||||
# Hard reset rpcd to finds the VPN menu and new ACL files.
|
||||
if [ -x "/etc/init.d/rpcd" ]; then
|
||||
/etc/init.d/rpcd restart >/dev/null 2>&1
|
||||
fi
|
||||
) &
|
||||
else
|
||||
logger -t luci-app-openvpn-plus "Image building state detected. Skipping live runtime triggers."
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in new issue
Block a user