/
linux_lab
/
ovpnft
Обзор
Документация
Войти
/
linux_lab
/
ovpnft
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
main
activate.sh
418 строк
12 KB
ophilon
base files
12 май 2026, 19:21
12 май 2026, 19:21
2945bf5
Код
Авторство
О чём код?
#!/bin/bash # OpenVPN user activation/deactivation script for nftables firewall # Usage: activate.sh [OPTIONS] USERNAME [USER_REMOTE_IP] set -e # Configuration NFT_CMD="/usr/sbin/nft" USERS_NFT_DIR="users.nft" TUN_PREFIX="tun" TUN_START=101 TUN_END=199 # Default values ACTION="help" USERNAME="" USER_REMOTE_IP="" VALIDATE=false # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Print colored message print_error() { echo -e "${RED}Error: $1${NC}" >&2 } print_success() { echo -e "${GREEN}$1${NC}" } print_info() { echo -e "${YELLOW}$1${NC}" } # Determine tun interface based on user IP get_tun_interface() { local user_ip="$1" if [ -z "$user_ip" ]; then print_error "User IP is required to determine tun interface" return 1 fi # Extract third octet from IP (assuming 192.168.X.Y pattern) local third_octet=$(echo "$user_ip" | cut -d. -f3) if [ -z "$third_octet" ]; then print_error "Could not parse IP address: $user_ip" return 1 fi # Convert to decimal and calculate tun number local tun_num=$((third_octet)) # Validate tun number range if [ $tun_num -lt $TUN_START ] || [ $tun_num -gt $TUN_END ]; then print_error "Tun interface number $tun_num is out of range ($TUN_START-$TUN_END)" return 1 fi # Format as tun number (tun101, tun102, etc.) printf "tun%d" $tun_num } # Find user IP and tun interface from existing nftables rules find_user_ip_and_tun() { local username="$1" local user_ip="" local tun_iface="" # Get the complete ruleset local ruleset if ! ruleset=$($NFT_CMD list ruleset 2>/dev/null); then print_error "Failed to list nftables ruleset" return 1 fi # Extract all IP addresses from rules related to this user # Look for patterns: user_${username} chain or table inet ${username} local user_ips user_ips=$(echo "$ruleset" | grep -E "(user_${username}|table inet ${username})" -A10 -B2 | grep -oP '192\.168\.\d+\.\d+' | sort -u) if [ -z "$user_ips" ]; then print_info "No IP addresses found for user $username in nftables rules" return 1 fi # Take the first IP (should be the user's IP) user_ip=$(echo "$user_ips" | head -1) if [ -z "$user_ip" ]; then print_error "Could not extract IP address from found patterns" return 1 fi # Extract tun interface from IP (third octet) local third_octet third_octet=$(echo "$user_ip" | cut -d. -f3) if [ -z "$third_octet" ]; then print_error "Could not parse IP address: $user_ip" return 1 fi # Validate tun number range if [ $third_octet -lt $TUN_START ] || [ $third_octet -gt $TUN_END ]; then print_error "Tun interface number $third_octet is out of range ($TUN_START-$TUN_END) for IP: $user_ip" return 1 fi tun_iface="tun${third_octet}" echo "$user_ip $tun_iface" return 0 } # Validate nftables configuration validate_nft() { local config_file="$1" if [ ! -f "$config_file" ]; then print_error "Configuration file not found: $config_file" return 1 fi print_info "Validating nftables configuration: $config_file" if ! $NFT_CMD -c -f "$config_file" 2>/dev/null; then print_error "Validation failed for: $config_file" return 1 fi print_success "Validation passed: $config_file" return 0 } # Add user rules add_user() { local username="$1" local user_ip="$2" local user_conf="$USERS_NFT_DIR/${username}.conf" local temp_conf="/tmp/${username}_nft.conf" if [ ! -f "$user_conf" ]; then print_error "User configuration not found: $user_conf" return 1 fi # Determine tun interface local tun_iface if ! tun_iface=$(get_tun_interface "$user_ip"); then return 1 fi print_info "Adding rules for user: $username" print_info "User IP: $user_ip" print_info "Tun interface: $tun_iface" # Create temporary configuration with substituted variables sed "s/\$USER_REMOTE_IP/$user_ip/g" "$user_conf" > "$temp_conf" # Add additional rules for integrating user chains into main chains cat >> "$temp_conf" << EOF # Integration rules for user $username # Add jump rules from main chains to user chains # Input validation chain for OpenVPN traffic # This chain allows input traffic from the user's IP address add chain inet filter user_${username} add rule inet filter user_${username} ip saddr $user_ip accept # Jump from input_openvpn chain to user chain for this tun interface # Insert at position 0 (beginning) to ensure it's processed before the default drop policy insert rule inet filter input_openvpn position 0 iif $tun_iface jump user_${username} # Jump rules for output chain - process user's outgoing traffic # Insert at position 0 to ensure user traffic is processed first insert rule inet filter output position 0 ip saddr $user_ip jump inet ${username} user_output # Jump rules for forward chain - process user's forwarded traffic # Insert at position 0 to ensure user traffic is processed before other forward rules insert rule inet filter forward position 0 ip saddr $user_ip jump inet ${username} user_forward # NAT rule for user traffic (add to main postrouting chain) add rule ip nat postrouting ip saddr $user_ip oif lo masquerade EOF # Validate if requested if [ "$VALIDATE" = true ]; then if ! validate_nft "$temp_conf"; then rm -f "$temp_conf" return 1 fi fi # Apply the configuration print_info "Applying nftables rules..." if ! $NFT_CMD -f "$temp_conf"; then print_error "Failed to apply nftables rules" rm -f "$temp_conf" return 1 fi # Clean up rm -f "$temp_conf" print_success "User $username added successfully" return 0 } # Delete user rules delete_user() { local username="$1" print_info "Deleting rules for user: $username" # Try to find user IP and tun interface from existing rules local user_info local user_ip="" local tun_iface="" if user_info=$(find_user_ip_and_tun "$username"); then user_ip=$(echo "$user_info" | awk '{print $1}') tun_iface=$(echo "$user_info" | awk '{print $2}') print_info "Found user IP: $user_ip" print_info "Determined tun interface: $tun_iface" else print_info "Could not find user information in ruleset, will delete all possible rules" fi # Create deletion script local delete_script="/tmp/delete_${username}_nft.conf" cat > "$delete_script" << EOF # Delete rules for user $username # Delete user table (if exists) delete table inet $username # Delete user chain in filter table (if exists) delete chain inet filter user_${username} # Delete jump rules from input_openvpn chain to user chain EOF if [ -n "$user_ip" ] && [ -n "$tun_iface" ]; then # Delete rules for specific user IP and tun interface cat >> "$delete_script" << EOF # Delete output jump rule delete rule inet filter output ip saddr $user_ip jump inet ${username} user_output # Delete forward jump rule delete rule inet filter forward ip saddr $user_ip jump inet ${username} user_forward # Delete input_openvpn jump rule for specific tun interface delete rule inet filter input_openvpn iif $tun_iface jump user_${username} # Delete NAT rule delete rule ip nat postrouting ip saddr $user_ip oif lo masquerade EOF else # Delete for all possible tun interfaces (fallback) print_info "Using fallback: deleting rules for all possible configurations" # Delete input_openvpn jump rules for all tun interfaces for ((i=TUN_START; i<=TUN_END; i++)); do tun_iface="tun${i}" echo "delete rule inet filter input_openvpn iif $tun_iface jump user_${username}" >> "$delete_script" done # Try to delete output and forward jump rules (may fail if they don't exist) # We can't specify IP without knowing it, so we'll try to delete by chain reference echo "# Try to delete output and forward jump rules" >> "$delete_script" echo "# Note: These may fail if rules don't exist" >> "$delete_script" echo "delete rule inet filter output jump inet ${username} user_output" >> "$delete_script" echo "delete rule inet filter forward jump inet ${username} user_forward" >> "$delete_script" echo "delete rule ip nat postrouting oif lo masquerade" >> "$delete_script" fi # Validate if requested if [ "$VALIDATE" = true ]; then if ! validate_nft "$delete_script"; then rm -f "$delete_script" return 1 fi fi # Apply deletion print_info "Removing nftables rules..." if ! $NFT_CMD -f "$delete_script" 2>/dev/null; then print_info "Some rules may not exist (this is normal if user was not active)" fi # Clean up rm -f "$delete_script" print_success "User $username removed successfully" return 0 } # Show usage show_help() { cat << EOF OpenVPN User Activation/Deactivation Script for NFTables Firewall Usage: $0 [OPTIONS] USERNAME [USER_REMOTE_IP] Options: --help Show this help message (default) --add Add user rules (requires USER_REMOTE_IP) --del Delete user rules --validate Validate nftables configuration before applying (can be used with --add or --del) Examples: $0 --add john.doe 192.168.5.100 # Add user john.doe with IP 192.168.5.100 $0 --del john.doe # Delete user john.doe $0 --add --validate jane.doe 192.168.3.50 # Add with validation $0 --help # Show this help Notes: - USER_REMOTE_IP is assigned by OpenVPN when user connects - The script determines tun interface automatically from IP address - User configuration must exist in $USERS_NFT_DIR/ - Requires nftables and appropriate permissions - Tun interface range: $TUN_START to $TUN_END (tun$TUN_START to tun$TUN_END) - When deleting, script searches nftables ruleset for user's IP address using pattern 192.168.X.Y to determine tun interface (tunX) EOF } # Parse command line arguments parse_args() { while [ $# -gt 0 ]; do case "$1" in --help) ACTION="help" shift ;; --add) ACTION="add" shift ;; --del|--delete) ACTION="del" shift ;; --validate) VALIDATE=true shift ;; *) if [ -z "$USERNAME" ]; then USERNAME="$1" elif [ -z "$USER_REMOTE_IP" ]; then USER_REMOTE_IP="$1" else print_error "Unexpected argument: $1" show_help exit 1 fi shift ;; esac done } # Main execution main() { parse_args "$@" # Check if nft command exists if ! command -v $NFT_CMD >/dev/null 2>&1; then print_error "nft command not found. Please install nftables." exit 1 fi case "$ACTION" in help) show_help ;; add) if [ -z "$USERNAME" ]; then print_error "Username is required for --add action" show_help exit 1 fi if [ -z "$USER_REMOTE_IP" ]; then print_error "USER_REMOTE_IP is required for --add action" show_help exit 1 fi add_user "$USERNAME" "$USER_REMOTE_IP" ;; del) if [ -z "$USERNAME" ]; then print_error "Username is required for --del action" show_help exit 1 fi delete_user "$USERNAME" ;; *) print_error "Unknown action: $ACTION" show_help exit 1 ;; esac } # Run main function main "$@"