Skip to content

Instantly share code, notes, and snippets.

@AdamsGH
Created October 20, 2024 16:53
Show Gist options
  • Save AdamsGH/d145a3249e3d5b708a9fe1796b865889 to your computer and use it in GitHub Desktop.
Save AdamsGH/d145a3249e3d5b708a9fe1796b865889 to your computer and use it in GitHub Desktop.
Automated installation and configuration of AmneziaWG for OpenWRT
#!/bin/bash
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_color() {
local color=$1
shift
echo -e "${color}$@${NC}"
}
# Function to display usage information
show_usage() {
print_color $BLUE "Usage: $0 <path_to_cfg_file> [interface_name] [peer_description] [zone] [-t|--test-route]"
print_color $BLUE "Options:"
print_color $BLUE " -t, --test-route Create a test route to 49.12.234.183"
exit 1
}
# Function to get parameter from config file
get_param() {
sed -n "/$1/{s/^[^=]*=//; s/^[[:space:]]*//; s/[[:space:]]*$//; p}" "$cfg_file"
}
# Function to add or update network interface
configure_interface() {
if ! uci show network | grep -q "network.${interface_name}=interface"; then
uci batch << EOI
set network.${interface_name}=interface
set network.${interface_name}.proto='amneziawg'
set network.${interface_name}.private_key='$(get_param 'PrivateKey')'
set network.${interface_name}.addresses='$(get_param 'Address')'
set network.${interface_name}.force_link='1'
set network.${interface_name}.delegate='0'
set network.${interface_name}.dns='$(get_param 'DNS' | tr ',' ' ')'
set network.${interface_name}.awg_jc='$(get_param 'Jc')'
set network.${interface_name}.awg_jmin='$(get_param 'Jmin')'
set network.${interface_name}.awg_jmax='$(get_param 'Jmax')'
set network.${interface_name}.awg_h1='$(get_param 'H1')'
set network.${interface_name}.awg_h2='$(get_param 'H2')'
set network.${interface_name}.awg_h3='$(get_param 'H3')'
set network.${interface_name}.awg_h4='$(get_param 'H4')'
EOI
print_color $GREEN "Interface ${interface_name} created."
else
print_color $YELLOW "Interface ${interface_name} already exists."
fi
}
# Function to add or update peer
configure_peer() {
local peer_exists=false
local peer_section=""
# Check if peer already exists
for peer in $(uci show network | grep "network.@amneziawg_${interface_name}\[" | cut -d. -f2 | cut -d= -f1); do
if [ "$(uci get network.${peer}.description)" = "${peer_description}" ]; then
peer_exists=true
peer_section="${peer}"
break
fi
done
local allowed_ips=$(get_param 'AllowedIPs')
IFS=',' read -ra ips <<< "$allowed_ips"
if [ "$peer_exists" = false ]; then
peer_section=$(uci add network amneziawg_${interface_name})
uci batch << EOI
set network.${peer_section}.description='${peer_description}'
set network.${peer_section}.public_key='$(get_param 'PublicKey')'
set network.${peer_section}.route_allowed_ips='0'
set network.${peer_section}.persistent_keepalive='25'
set network.${peer_section}.endpoint_host='$(get_param 'Endpoint' | cut -d':' -f1)'
set network.${peer_section}.endpoint_port='$(get_param 'Endpoint' | cut -d':' -f2)'
set network.${peer_section}.persistent_keepalive='60'
EOI
print_color $GREEN "Peer ${peer_description} added to interface ${interface_name}."
else
uci batch << EOI
set network.${peer_section}.public_key='$(get_param 'PublicKey')'
set network.${peer_section}.endpoint_host='$(get_param 'Endpoint' | cut -d':' -f1)'
set network.${peer_section}.endpoint_port='$(get_param 'Endpoint' | cut -d':' -f2)'
set network.${peer_section}.persistent_keepalive='60'
delete network.${peer_section}.allowed_ips
EOI
print_color $YELLOW "Peer ${peer_description} updated in interface ${interface_name}."
fi
# Add allowed IPs
for ip in "${ips[@]}"; do
uci add_list network.${peer_section}.allowed_ips="${ip//[[:space:]]/}"
done
}
# Function to configure firewall
configure_firewall() {
print_color $BLUE "Configuring firewall..."
# Create or update the zone for the new interface
zone_config=$(uci show firewall | grep "@zone\[.*\].name='${zone_name}'")
if [ -z "$zone_config" ]; then
# Create new zone
new_zone=$(uci add firewall zone)
uci batch << EOI
set firewall.$new_zone.name='${zone_name}'
set firewall.$new_zone.input='REJECT'
set firewall.$new_zone.output='ACCEPT'
set firewall.$new_zone.forward='REJECT'
set firewall.$new_zone.masq='1'
set firewall.$new_zone.mtu_fix='1'
set firewall.$new_zone.network='${interface_name}'
EOI
print_color $GREEN "Created new firewall zone ${zone_name} for interface ${interface_name}."
else
# Update existing zone
existing_zone=$(echo "$zone_config" | cut -d. -f2 | cut -d= -f1)
uci set firewall.$existing_zone.network="${interface_name}"
print_color $YELLOW "Updated existing firewall zone ${zone_name} with interface ${interface_name}."
fi
# Remove all existing forwarding rules for this zone
existing_forwards=$(uci show firewall | grep "@forwarding\[[0-9]\+\]" | grep "\.dest='${zone_name}'")
if [ -n "$existing_forwards" ]; then
print_color $YELLOW "Removing existing forwarding rules for ${zone_name}..."
echo "$existing_forwards" | while read -r line; do
section=$(echo "$line" | cut -d. -f2 | cut -d= -f1)
uci delete firewall.$section
done
fi
# Add a single new forwarding rule
new_forward=$(uci add firewall forwarding)
uci set firewall.$new_forward.src='lan'
uci set firewall.$new_forward.dest="${zone_name}"
print_color $GREEN "Added new forwarding rule from LAN to ${zone_name}."
print_color $GREEN "Firewall settings for ${zone_name} zone have been configured."
}
# Function to create test route
create_test_route() {
local address=$(get_param 'Address')
local gateway=$(echo $address | awk -F'[./]' '{print $1"."$2"."$3".1"}')
uci batch << EOI
set network.test_route=route
set network.test_route.interface='${interface_name}'
set network.test_route.target='49.12.234.183/32'
set network.test_route.gateway='${gateway}'
EOI
print_color $GREEN "Test route created:"
print_color $BLUE " Target: 49.12.234.183/32"
print_color $BLUE " Gateway: ${gateway}"
print_color $BLUE " Interface: ${interface_name}"
print_color $YELLOW "You can test the connection using: ${NC}curl 49.12.234.183"
}
# Main execution
# Parse command line arguments
CREATE_TEST_ROUTE=false
while [[ $# -gt 0 ]]; do
case $1 in
-t|--test-route)
CREATE_TEST_ROUTE=true
shift
;;
-h|--help)
show_usage
;;
*)
if [ -z "$cfg_file" ]; then
cfg_file="$1"
elif [ -z "$interface_name" ]; then
interface_name="$1"
elif [ -z "$peer_description" ]; then
peer_description="$1"
elif [ -z "$zone_name" ]; then
zone_name="$1"
else
print_color $RED "Unknown argument: $1"
show_usage
fi
shift
;;
esac
done
# Check for minimum required arguments
[ -z "$cfg_file" ] && show_usage
# Set default values if not provided
interface_name="${interface_name:-AWG}"
peer_description="${peer_description:-openwrt_router}"
zone_name="${zone_name:-awg}"
print_color $BLUE "Starting Amnezia WireGuard setup..."
configure_interface
configure_peer
uci commit network
configure_firewall
uci commit firewall
if [ "$CREATE_TEST_ROUTE" = true ]; then
create_test_route
uci commit network
fi
print_color $YELLOW "Restarting network..."
/etc/init.d/network restart
print_color $GREEN "Amnezia WireGuard setup completed."
#!/bin/bash
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_color() {
local color=$1
shift
echo -e "${color}$@${NC}"
}
# Function to get release info
get_release_info() {
curl -s "https://api.github.com/repos/Slava-Shchipunov/awg-openwrt/releases/latest"
}
# Function to extract version from release info
get_latest_version() {
echo "$1" | grep -o '"tag_name": "v[^"]*"' | sed 's/"tag_name": "v//;s/"//'
}
# Function to get available files
get_available_files() {
echo "$1" | grep -o '"browser_download_url": "[^"]*\.ipk"' | sed 's/"browser_download_url": "//;s/"//'
}
# Function to get all architecture groups and their options
get_all_arch_groups_and_options() {
echo "$AVAILABLE_FILES" |
sed -E 's/.*_v[^_]*_//; s/\.ipk$//' |
sort -u |
awk -F'_' '{
group = $1;
option = $2;
for(i=3; i<=NF; i++) option = option "_" $i;
if(option == "") option = "generic";
if(group == "x86" && option ~ /^64/) {
print group "|64";
} else {
print group "|" option;
}
}' |
sort -t'|' -k1,1 -k2,2 |
uniq
}
# Function to print groups and options
print_groups_and_options() {
local current_group=""
while IFS='|' read -r group option; do
if [ "$group" != "$current_group" ]; then
if [ -n "$current_group" ]; then
echo
fi
print_color $YELLOW "Group: $group"
current_group="$group"
fi
if [ "$group" = "x86" ] && [ "$option" = "64" ]; then
echo " 64"
else
echo " $option"
fi
done
}
# Function to get all architecture groups
get_all_arch_groups() {
echo "$AVAILABLE_FILES" | sed -E 's/.*_v[^_]*_([^_]+)(_.*\.ipk|\.ipk)/\1/' | sort -u
}
# Function to get available options for a group
get_options() {
local arch_group=$1
echo "$AVAILABLE_FILES" | grep -E "_v${LATEST_VERSION}_${arch_group}" |
sed -E "s/.*_v${LATEST_VERSION}_${arch_group}_//; s/\.ipk$//" |
sort -u |
sed '/^$/d'
}
# Function to install packages
install_packages() {
print_color $GREEN "Installing packages..."
for package in awg/*.ipk; do
if [ -f "$package" ]; then
print_color $BLUE "Installing $package"
opkg install "$package"
if [ $? -eq 0 ]; then
print_color $GREEN "Successfully installed $package"
else
print_color $RED "Failed to install $package"
fi
fi
done
}
# Function to display usage
usage() {
print_color $BLUE "Usage: $0 <arch_group> [arch_option] [-i|--install] [-k|--keep]"
print_color $BLUE "Example: $0 arm cortex-a9_neon_zynq_generic -i"
print_color $BLUE "Or: $0 x86_64 --install --keep"
print_color $BLUE "Use '$0 list' to see all available architecture groups and options"
print_color $BLUE "Options:"
print_color $BLUE " -i, --install Install packages after download"
print_color $BLUE " -k, --keep Keep the download directory after installation"
}
# Main script starts here
INSTALL=false
KEEP=false
ARCH_GROUP=""
ARCH_OPTION=""
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-i|--install)
INSTALL=true
shift
;;
-k|--keep)
KEEP=true
shift
;;
list|-h|--help)
RELEASE_INFO=$(get_release_info)
LATEST_VERSION=$(get_latest_version "$RELEASE_INFO")
AVAILABLE_FILES=$(get_available_files "$RELEASE_INFO")
print_color $GREEN "Available architecture groups and options:"
get_all_arch_groups_and_options | print_groups_and_options
exit 0
;;
*)
if [ -z "$ARCH_GROUP" ]; then
ARCH_GROUP="$1"
elif [ -z "$ARCH_OPTION" ]; then
ARCH_OPTION="$1"
else
print_color $RED "Unknown argument: $1"
usage
exit 1
fi
shift
;;
esac
done
if [ -z "$ARCH_GROUP" ]; then
usage
exit 1
fi
RELEASE_INFO=$(get_release_info)
LATEST_VERSION=$(get_latest_version "$RELEASE_INFO")
AVAILABLE_FILES=$(get_available_files "$RELEASE_INFO")
if [ -z "$LATEST_VERSION" ]; then
print_color $RED "Failed to extract version from release info."
exit 1
fi
print_color $GREEN "Latest version: $LATEST_VERSION"
if [ -z "$ARCH_OPTION" ]; then
options=$(get_options $ARCH_GROUP)
if [ -z "$options" ]; then
FULL_ARCH="${ARCH_GROUP}"
else
print_color $YELLOW "Available options for $ARCH_GROUP:"
echo "$options"
exit 0
fi
else
FULL_ARCH="${ARCH_GROUP}_${ARCH_OPTION}"
fi
# Download files
rm -rf awg
mkdir -p awg
for FILE in amneziawg-tools kmod-amneziawg luci-app-amneziawg; do
FULL_FILE=$(echo "$AVAILABLE_FILES" | grep "/${FILE}_v${LATEST_VERSION}_${FULL_ARCH}.ipk$")
if [ -n "$FULL_FILE" ]; then
OUTPUT_FILE="awg/${FILE}_v${LATEST_VERSION}_${FULL_ARCH}.ipk"
if curl -s -L -o "$OUTPUT_FILE" "$FULL_FILE"; then
print_color $GREEN "Downloaded: ${FILE}_v${LATEST_VERSION}_${FULL_ARCH}.ipk"
else
print_color $RED "Failed to download $FILE"
fi
else
print_color $YELLOW "File $FILE for architecture $FULL_ARCH not found"
fi
done
print_color $GREEN "Download completed."
if [ "$INSTALL" = true ]; then
install_packages
if [ "$KEEP" = false ]; then
print_color $YELLOW "Removing download directory..."
rm -rf awg
print_color $GREEN "Download directory removed."
fi
else
print_color $YELLOW "Packages downloaded but not installed. Use -i or --install to install packages."
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment