name: Set up WireGuard description: Set up WireGuard connection inputs: endpoint: description: Endpoint in the HOST:PORT format required: true endpoint_public_key: description: Public key of the endpoint required: true ips: description: Comma-separated list of IP addresses required: true allowed_ips: description: Comma-separated list of netmasks required: true private_key: description: Private key required: true preshared_key: description: Preshared key required: false keepalive: description: Useful for NAT traversal required: false runs: using: composite steps: - run: | set -o errexit -o pipefail -o nounset readonly endpoint='${{ inputs.endpoint }}' readonly endpoint_public_key='${{ inputs.endpoint_public_key }}' readonly ips='${{ inputs.ips }}' readonly allowed_ips='${{ inputs.allowed_ips }}' readonly private_key='${{ inputs.private_key }}' readonly preshared_key='${{ inputs.preshared_key }}' readonly keepalive='${{ inputs.keepalive }}' readonly minport=51000 readonly maxport=51999 ifname="wg$( openssl rand -hex 4 )" readonly ifname port="$( shuf "--input-range=$minport-$maxport" --head-count=1 )" readonly port via_systemd() { local netdev_path netdev_path="/etc/systemd/network/$ifname.netdev" local network_path network_path="/etc/systemd/network/$ifname.network" local netdev_contents netdev_contents=" [NetDev] Name=$ifname Kind=wireguard Description=WireGuard tunnel $ifname [WireGuard] ListenPort=$port PrivateKey=$private_key [WireGuardPeer] Endpoint=$endpoint PublicKey=$endpoint_public_key AllowedIPs = $allowed_ips" if [ -n "$preshared_key" ]; then netdev_contents="$netdev_contents PresharedKey=$preshared_key" fi if [ -n "$keepalive" ]; then netdev_contents="$netdev_contents PersistentKeepalive=$keepalive" fi local network_contents network_contents=" [Match] Name=$ifname [Network]" local delim=, local ip while IFS= read -d "$delim" -r ip; do network_contents="$network_contents Address=$ip" done < <( printf -- "%s$delim\\0" "$ips" ) sudo touch -- "$netdev_path" sudo chown -- root:systemd-network "$netdev_path" sudo chmod -- 0640 "$netdev_path" sudo touch -- "$network_path" echo "$netdev_contents" | sudo tee -- "$netdev_path" > /dev/null echo "$network_contents" | sudo tee -- "$network_path" > /dev/null sudo systemctl restart systemd-networkd sudo systemctl status systemd-networkd } install_wg_tools() { sudo apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends wireguard-tools } readonly private_key_path=/tmp/private.key readonly preshared_key_path=/tmp/preshared.key wg_tools_cleanup() { rm -f -- "$private_key_path" rm -f -- "$preshared_key_path" } via_wg_tools() { install_wg_tools trap wg_tools_cleanup EXIT ( set -o errexit -o nounset -o pipefail umask 0077 echo "$private_key" > "$private_key_path" if [ -n "$preshared_key" ]; then echo "$preshared_key" > "$preshared_key_path" fi ) sudo ip link add dev "$ifname" type wireguard local delim=, local ip while IFS= read -d "$delim" -r ip; do sudo ip addr add "$ip" dev "$ifname" done < <( printf -- "%s$delim\\0" "$ips" ) sudo wg set "$ifname" \ listen-port "$port" \ private-key "$private_key_path" additional_wg_args=() if [ -n "$preshared_key" ]; then additional_wg_args+=(preshared-key "${preshared_key_path}") fi if [ -n "$keepalive" ]; then additional_wg_args+=(persistent-keepalive "${keepalive}") fi sudo wg set "$ifname" \ peer "$endpoint_public_key" \ endpoint "$endpoint" \ allowed-ips "$allowed_ips" \ ${additional_wg_args[@]+"${additional_wg_args[@]}"} sudo ip link set "$ifname" up # Add routes for allowed_ips for i in ${allowed_ips//,/ }; do sudo ip route replace "$i" dev "$ifname"; done } # systemd-networkd greets me with 'Temporary failure in name # resolution' on Bionic when using a hostname instead of an IP address # for endpoint. God knows why! #via_systemd via_wg_tools shell: bash branding: icon: star color: green