#!/usr/bin/with-contenv bashio # ============================================================================== # Community Hass.io Add-ons: WireGuard # Creates the interface configuration # ============================================================================== declare -a list declare addresses declare allowed_ips declare config declare config_dir declare dns declare endpoint declare filename declare fwmark declare host declare interface declare keep_alive declare mtu declare name declare peer_private_key declare peer_public_key declare port declare post_down declare post_up declare pre_down declare pre_shared_key declare pre_up declare server_private_key declare server_public_key declare table if ! bashio::fs.directory_exists '/ssl/wireguard'; then mkdir -p /ssl/wireguard || bashio::exit.nok "Could not create wireguard storage folder!" fi # Get interface and config file location interface="wg0" if bashio::config.has_value "server.interface"; then interface=$(bashio::config "server.interface") fi config="/etc/wireguard/${interface}.conf" # Start creation of configuration echo "[Interface]" > "${config}" # Check if at least 1 address is specified if ! bashio::config.has_value 'server.addresses'; then bashio::exit.nok 'You need at least 1 address configured for the server' fi # Add all server addresses to the configuration for address in $(bashio::config 'server.addresses'); do [[ "${address}" == *"/"* ]] || address="${address}/24" echo "Address = ${address}" >> "${config}" done # Add all server DNS addresses to the configuration if bashio::config.has_value 'server.dns'; then for dns in $(bashio::config 'server.dns'); do echo "DNS = ${dns}" >> "${config}" done else dns=$(bashio::dns.host) echo "DNS = ${dns}" >> "${config}" fi # Get the server's private key if bashio::config.has_value 'server.private_key'; then server_private_key=$(bashio::config 'server.private_key') else if ! bashio::fs.file_exists '/ssl/wireguard/private_key'; then umask 077 || bashio::exit.nok "Could not set a proper umask" wg genkey > /ssl/wireguard/private_key || bashio::exit.nok "Could not generate private key!" fi server_private_key=$(> "${config}" # Get DNS for client configurations if bashio::config.has_value 'server.dns'; then dns=$(bashio::config "server.dns | join(\", \")") fi # Status API Storage if ! bashio::fs.directory_exists '/var/lib/wireguard'; then mkdir -p /var/lib/wireguard \ || bashio::exit.nok "Could not create status API storage folder" fi # Fetch all the peers for peer in $(bashio::config 'peers|keys'); do # Check if at least 1 address is specified if ! bashio::config.has_value "peers[${peer}].addresses"; then bashio::exit.nok "You need at least 1 address configured for ${name}" fi name=$(bashio::config "peers[${peer}].name") config_dir="/ssl/wireguard/${name}" endpoint=$(bashio::config "peers[${peer}].endpoint") fwmark=$(bashio::config "peers[${peer}].fwmark") host=$(bashio::config 'server.host') port=$(bashio::addon.port "51820/udp") pre_shared_key=$(bashio::config "peers[${peer}].pre_shared_key") # Create directory for storing client configuration mkdir -p "${config_dir}" || bashio::exit.nok "Failed creating client folder for ${name}" # Get the private key peer_private_key="" if bashio::config.has_value "peers[${peer}].private_key"; then peer_private_key=$(bashio::config "peers[${peer}].private_key") elif ! bashio::config.has_value "peers[${peer}].public_key"; then # If a public key is not provided, try get a private key from disk # or generate one if needed. if ! bashio::fs.file_exists "${config_dir}/private_key"; then umask 077 || bashio::exit.nok "Could not set a proper umask" wg genkey > "${config_dir}/private_key" || bashio::exit.nok "Could not generate private key for ${name}!" fi peer_private_key=$(<"${config_dir}/private_key") fi # Get the public key peer_public_key="" if bashio::config.has_value "peers[${peer}].public_key"; then peer_public_key=$(bashio::config "peers[${peer}].public_key") elif bashio::var.has_value "${peer_private_key}"; then peer_public_key=$(wg pubkey <<< "${peer_private_key}") fi # Get peer addresses list=() for address in $(bashio::config "peers[${peer}].addresses"); do [[ "${address}" == *"/"* ]] || address="${address}/24" list+=("${address}") done addresses=$(IFS=", "; echo "${list[*]}") # Determine allowed IPs for server side config, by default use # peer defined addresses. list=() if bashio::config.has_value "peers[${peer}].allowed_ips"; then # Use allowed IP's defined by the user. for address in $(bashio::config "peers[${peer}].allowed_ips"); do [[ "${address}" == *"/"* ]] || address="${address}/32" list+=("${address}") done else for address in $(bashio::config "peers[${peer}].addresses"); do [[ "${address}" == *"/"* ]] || address="${address}/32" list+=("${address}") done fi allowed_ips=$(IFS=", "; echo "${list[*]}") # Determine persistent keep alive keep_alive=25 if bashio::config.has_value "peers[${peer}].persistent_keep_alive"; then keep_alive=$(bashio::config "peers[${peer}].persistent_keep_alive") fi # Start writing peer information in server config { echo "[Peer]" echo "PublicKey = ${peer_public_key}" echo "AllowedIPs = ${allowed_ips}" echo "PersistentKeepalive = ${keep_alive}" bashio::config.has_value "peers[${peer}].pre_shared_key" \ && echo "PreSharedKey = ${pre_shared_key}" bashio::config.has_value "peers[${peer}].endpoint" \ && echo "Endpoint = ${endpoint}" echo "" } >> "${config}" # Generate client configuration # Determine allowed IPs for client configuration allowed_ips="0.0.0.0/0" if bashio::config.has_value "peers[${peer}].client_allowed_ips"; then allowed_ips=$( bashio::config "peers[${peer}].client_allowed_ips | join(\", \")" ) fi # Write client configuration file { echo "[Interface]" bashio::fs.file_exists "${config_dir}/private_key" \ && echo "PrivateKey = ${peer_private_key}" echo "Address = ${addresses}" echo "DNS = ${dns}" bashio::config.has_value "peers[${peer}].fwmark" \ && echo "FwMark = ${fwmark}" echo "" echo "[Peer]" echo "PublicKey = ${server_public_key}" echo "Endpoint = ${host}:${port}" echo "AllowedIPs = ${allowed_ips}" echo "PersistentKeepalive = ${keep_alive}" echo "" } > "${config_dir}/client.conf" # Generate QR code with client configuration qrencode -t PNG -o "${config_dir}/qrcode.png" < "${config_dir}/client.conf" # Store client name for the status API based on public key filename=$(sha1sum <<< "${peer_public_key}" | awk '{ print $1 }') echo -n "${name}" > "/var/lib/wireguard/${filename}" done