#!/bin/bash # this can be run like: # bash -c "$(curl -fsSL https://raw.github.com/spr-networks/super/master/virtual_install.sh)" # run with SKIP_VPN=1 to skip vpn peer setup # run with SKIP_DNS_BLOCK=1 to disable dns block, default is hosts,ads # add custom blocks with DNS_BLOCK=hosts,ads,facebook # if configs are already setup it'll only show the login info # for a clean reset: # docker compose -f docker-compose-virt.yml down && rm -rf configs && ./virtual_install.sh if [ $UID -ne 0 ]; then echo "[-] run as root or with sudo" exit fi install_deps() { # install upstream docker apt-get update apt-get -y install ca-certificates curl gnupg install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ tee /etc/apt/sources.list.d/docker.list > /dev/null apt update apt-get -y install --no-install-recommends docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin apt install -y curl git jq qrencode iproute2 wireguard-tools git clone https://github.com/spr-networks/super.git cd super # overwrite docker compose.yml cp docker-compose-virt.yml docker-compose.yml } # if not git dir is available if [ ! -f "./docker-compose-virt.yml" ]; then install_deps fi docker compose 2>/dev/null >/dev/null HAS_NEWDC=$? if [ $HAS_NEWDC -ne 0 ]; then echo "[-] A newer version of docker is required" exit 1 fi # generate default configs if [ ! -f configs/base/config.sh ]; then echo "[+] generating configs..." cp -R base/template_configs/ configs/ mv configs/base/virtual-config.sh configs/base/config.sh # generate dhcp config ./configs/scripts/gen_coredhcp_yaml.sh > configs/dhcp/coredhcp.yml touch configs/base/.setup_done fi CONTAINER_CHECK=superapi # pull and start containers docker inspect "$CONTAINER_CHECK" > /dev/null 2>&1 if [ $? -eq 1 ]; then echo "[+] starting spr..." docker compose -f docker-compose-virt.yml pull docker compose -f docker-compose-virt.yml up -d else echo "[+] spr already running" fi # external ip DEV=eth0 DEV=$(ip route get 1.1.1.1 | grep -oP 'dev \K\w+' -m1) # NOTE if this is not eth0 - change config.sh # verify its a public ip address EXTERNAL_IP=$(ip addr show dev $DEV | grep -oP "inet \K[0-9\.]+" -m1) EXTERNAL_IP=$(echo "$EXTERNAL_IP" | grep -P '^(?!^0\.)(?!^10\.)(?!^100\.6[4-9]\.)(?!^100\.[7-9]\d\.)(?!^100\.1[0-1]\d\.)(?!^100\.12[0-7]\.)(?!^127\.)(?!^169\.254\.)(?!^172\.1[6-9]\.)(?!^172\.2[0-9]\.)(?!^172\.3[0-1]\.)(?!^192\.0\.0\.)(?!^192\.0\.2\.)(?!^192\.88\.99\.)(?!^192\.168\.)(?!^198\.1[8-9]\.)(?!^198\.51\.100\.)(?!^203.0\.113\.)(?!^22[4-9]\.)(?!^23[0-9]\.)(?!^24[0-9]\.)(?!^25[0-5]\.)(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$') EXTERNAL_PORT=8000 if [ ${#EXTERNAL_IP} -eq 0 ]; then echo "[-] failed to get external ip for $DEV" echo -n "[?] fetch from https://ifconfig.me? [Y/n] " read YN if [ "$YN" == "n" ] || [ "$YN" == "N" ] ; then EXTERNAL_IP="127.0.0.1" echo "[+] setting endpoint to $EXTERNAL_IP, change this manually in your config" else EXTERNAL_IP=$(curl -s "https://ifconfig.me") fi fi # check spr is running SPR_DIR=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "$CONTAINER_CHECK") if [ ${#SPR_DIR} -eq 0 ]; then echo "[-] $CONTAINER_CHECK not running" SPR_DIR=/home/spr/super exit fi # only generate user if init if [ ! -f $SPR_DIR/configs/auth/auth_users.json ]; then echo "[+] generating admin password" PASSWORD=$(cat /dev/urandom | tr -dc '[:alpha:]' | fold -w ${1:-16} | head -n 1) echo "{\"admin\" : \"$PASSWORD\"}" > $SPR_DIR/configs/auth/auth_users.json echo "[+] generating token..." TOKEN=$(dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64) echo "[{\"Name\": \"admin\", \"Token\": \"$TOKEN\", \"Expire\": 0}]" > $SPR_DIR/configs/auth/auth_tokens.json else PASSWORD=$(cat "$SPR_DIR/configs/auth/auth_users.json" | jq -r .admin) TOKEN=$(cat "$SPR_DIR/configs/auth/auth_tokens.json" | jq -r '.[0].Token') fi # dns block. default: hosts,ads # example, run with: DNS_BLOCK="hosts,malware,facebook,redirect" if [ -z "$DNS_BLOCK" ]; then DNS_BLOCK="hosts,ads" fi if [ ! -z "$DNS_BLOCK" ] && [ -z $SKIP_DNS_BLOCK ]; then urls=() _IFS=$IFS IFS=',' for f in $DNS_BLOCK; do if [ "$f" == "hosts" ]; then urls+=("https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts") else urls+=( "https://raw.githubusercontent.com/blocklistproject/Lists/master/${f}.txt" ) fi done IFS=$_IFS CONF='{"BlockLists": [], "PermitDomains": [], "BlockDomains": [], "ClientIPExclusions": null}' for url in ${urls[@]}; do CONF=$(echo $CONF | jq ".BlockLists |= . + [{\"URI\": \"$url\", \"Enabled\": true, \"Tags\": []}]") done echo $CONF | jq . > $SPR_DIR/state/dns/block_rules.json fi echo "[+] login information:" echo "==========================================================" HOST=$EXTERNAL_IP if [ $HOST == "127.0.0.1" ]; then HOST="sprvirtual" fi echo " http tunnel: ssh $HOST -N -L $EXTERNAL_PORT:127.0.0.1:$EXTERNAL_PORT" echo " url: http://localhost:$EXTERNAL_PORT/" echo " username: admin" echo " password: $PASSWORD" if [ ! -z "$TOKEN" ]; then echo " token: $TOKEN" fi echo "==========================================================" # if set - exit if [ ! -z $SKIP_VPN ]; then exit fi sleep 1 while grep "= privkey" $SPR_DIR/configs/wireguard/wg0.conf > /dev/null; do echo "... Waiting for wireguard service to start and initialize" sleep 5 done # set External IP for SPR for later, so users do not present # with the internal endpoint IP. curl 'http://localhost:8000/plugins/wireguard/endpoints' -X 'PUT' -H "Authorization: Bearer ${TOKEN}" --data-raw "[\"${EXTERNAL_IP}\"]" #only show confirm if its the first one NUM_PEERS=$(grep '^\[Peer\]' $SPR_DIR/configs/wireguard/wg0.conf 2>/dev/null | wc -l) echo "[+] num peers already configured: $NUM_PEERS" # Use the API to generate a wireguard peer RET=$(curl -s -H "Authorization: Bearer ${TOKEN}" -X PUT http://localhost:8000/plugins/wireguard/peer --data '{}') PRIVATE_KEY=$(echo $RET | jq -r .Interface.PrivateKey) PUBLIC_KEY=$(echo $PRIVATE_KEY | wg pubkey) PUBLIC_KEY_ESCAPED=$(echo \"${PUBLIC_KEY}\" | jq -r @uri) CLIENT_IP=$(echo $RET | jq -r .Interface.Address) SERVER_PUBLIC_KEY=$(echo $RET | jq -r .Peer.PublicKey) PRESHARED_KEY=$(echo $RET | jq -r .Peer.PresharedKey) DNS_IP=$(echo $RET | jq -r .Interface.DNS) # Update the Groups for the Device and Name RET=$(curl -s -H "Authorization: Bearer ${TOKEN}" -X PUT http://localhost:8000/device?identity=${PUBLIC_KEY_ESCAPED} --data "{\"Policies\": [\"wan\", \"dns\"], \"Name\": \"peer${NUM_PEERS}\"}") # wg client config _IFS=$IFS IFS='\n' CONF=$(cat << EOF [Interface] PrivateKey = $PRIVATE_KEY Address = $CLIENT_IP DNS = 192.168.2.1 [Peer] PublicKey = $SERVER_PUBLIC_KEY AllowedIPs = 0.0.0.0/0, ::/0 Endpoint = $EXTERNAL_IP:51280 PersistentKeepalive = 25 PresharedKey = $PRESHARED_KEY EOF ) IFS=$_IFS echo -e "\n[+] WireGuard config (save this as wg.conf & import in client):\n" echo -e "$CONF\n" >/dev/tty printf "Show QR Code? [Y/n] " </dev/tty read -rn1 if [[ ! $REPLY =~ [nN](oO)* ]]; then echo -e "[+] WireGuard QR Code (import in iOS & Android app):\n" echo -e "$CONF" | qrencode -t ansiutf8 fi # reload dns if we have modified blocks if [ ! -z "$DNS_BLOCK" ]; then docker compose -f docker-compose-virt.yml restart dns >/dev/null 2>&1 & fi