summaryrefslogtreecommitdiff
path: root/wireguard-tools/README
blob: b4f44597acff0a695943a501a2e458ea99562cfd (plain)
    1 README for WireGuard
    2 
    3   WireGuard is a thrilling VirtualPrivateNetwork option.
    4 
    5   It uses stateless UDP connections, and looks like a server socket, for
    6   example in output of "ss -l", but also "iptables -nvL".  It uses today's
    7   state-of-the-art algorithms, is extremely simple to setup and maintain, and
    8   has really nifty properties.  For example, you can load /dev/null as the key
    9   to make the VPN unusable, then just put back the correct key and it
   10   functions again.  You can also create a VPN with a single command line (plus
   11   firewall).
   12 
   13   It is possible to create point-to-point connections where the endpoints can
   14   communicate only with each other, but on the other hand dedicated "servers"
   15   can be used to which all traffic can be forwarded, so that laptops and other
   16   end-devices can be boxed into a totally detached environment, having
   17   internet access only through (the) VPN(s).
   18 
   19   In all cases you need the kernel option
   20 
   21     CONFIG_WIREGUARD=y
   22 
   23   and generate keys:
   24 
   25     # wg genkey | tee private.key | wg pubkey > public.key
   26 
   27   You should also create a preshared key (may not work otherwise with
   28   software before 2021-03-15):
   29 
   30     # wg genpsk
   31 
   32   Nothing magic about the keys, base64 encoded random of the correct
   33   length (should do; and except for pubkey, which applies algorithms).
   34 
   35 Simple point-to-point VPN
   36 
   37   Say this is a VPN of two boxes plus broadcast, server on 10.0.0.2,
   38   laptop on 10.0.0.1.  The laptop has no fixed IP:
   39 
   40   Server.conf:
   41     [Interface]
   42     PrivateKey = SERVER-PRIKEY
   43     ListenPort = SERVER-PORT
   44 
   45     [Peer]
   46     PublicKey = LAPTOP-PUBKEY
   47     # (Or only 10.0.0.1/32)
   48     AllowedIPs = 10.0.0.0/30
   49 
   50   Laptop.conf:
   51     [Interface]
   52     PrivateKey = LAPTOP-PRIKEY
   53     # (Actually unused)
   54     ListenPort = LAPTOP-PORT
   55 
   56     [Peer]
   57     PublicKey = SERVER-PUBKEY
   58     Endpoint = SERVER-IP:SERVER-PORT
   59     AllowedIPs = 10.0.0.2/32
   60 
   61   Assuming these are the first interfaces we ever created:
   62 
   63     # wg setconf wg0 Server.conf
   64 
   65   We need some firewall rules.  For the case as shown here no forwarding or
   66   masquerading is required -- and it is _never_, but on those peers which play
   67   a server rule!  The Laptop should get away with
   68 
   69     # ip link add dev wg0 type wireguard
   70     # ip address add 10.0.0.1/30 dev wg0
   71     # iptables -A OUTPUT -o wg0 -j ACCEPT
   72     # # not even iptables -A INPUT -i wg0 -j ACCEPT
   73     # iptables -A OUTPUT -p udp --dst SRV-IP --dport SRV-PORT -j ACCEPT
   74     # ip link set wg0 up
   75     # ip route add 10.0.0.1 dev wg0
   76 
   77   That is it (beat me if i am wrong)!  Different iptables on the server:
   78 
   79     # iptables -I INPUT -i wg0 -j ACCEPT
   80     # # not even iptables -A OUTPUT -o wg0 -j ACCEPT
   81     # iptables -A INPUT -p udp --dport SRV-PORT -j ACCEPT
   82 
   83   Finished.  You could track the endpoint as they show up, and update the
   84   rules with the exact address of the endpoint(s).  Like this the last shown
   85   rule of the server can apply blacklisting rules.  This works easily because
   86   once a handshake is completed the defined ListenPort and thus NETFILTER is
   87   bypassed (at filter level), and only fewest packets actually show up on
   88   --dport SRV-PORT.  A working watchdog below. 
   89 
   90 End-user having access only via VPN
   91 
   92   In fact this is easy.  Of course you can create a wg, then a network
   93   namespace, then move the wg to that namespace via "ip link set wg0 netns
   94   NSNAME", then add the default route there via "ip -n NSNAME route add
   95   default dev wg0", and be done with it.  Linux even seems to allow to move
   96   the physical hardware to a network namespace, then go the reverse way with
   97   the new wg, leaving only wg in the base namespace, and the physical devices
   98   boxed somewhere else.  Really important differences are:
   99 
  100     - The laptop must change the AllowedIPs of the server [Peer] to
  101 
  102         AllowedIPs = 0.0.0.0/0
  103 
  104       Only like this all the traffic is forwarded to the server.
  105 
  106     - The server now needs forwarding and masquerading enabled:
  107 
  108         # sysctl -w net.ipv4.conf.ETH0.forwarding=1
  109         # sysctl -w net.ipv4.conf.WG0.forwarding=1
  110         # iptables -A FORWARD -i WG0 -o ETH0 -j ACCEPT
  111         # iptables -A FORWARD -o WG0 -i ETH0 -j ACCEPT
  112         # iptables -t nat -A POSTROUTING -o ETH0 -j MASQUERADE
  113 
  114       Exchange WG0/ETH0 with your devices.  You may want to have a final
  115       FORWARDING rule like
  116 
  117         # iptables -A FORWARD -j REJECT --reject-with icmp-proto-unreachable
  118 
  119   You may want to create an additional veth pair that links into the
  120   namespace, so that a local DNS proxy like dnsmasq could serve the DNS of
  121   that VPN network namespace as well as any other namespace ("interface
  122   NSNAME" in dnsmasq.conf).  No need to start multiple instances, just share
  123   the DNS cache.  Of course that single dnsmasq instance could also have
  124   a configured upstream that is reached via VPN, maybe just another channel.
  125   Luckily "ip link" and "ip netns" names can coexist, so:
  126 
  127       # Placing this in 10.4.0.8/30
  128       ip=ip ns=NSNAME 1=10.4.0.9 2=10.4.0.10 p_domain=53
  129       ...
  130 
  131        ${ip} link add ${ns} type veth peer name ${ns}_peer
  132        ${ip} link set ${ns}_peer netns ${ns}
  133 
  134        ${ip} addr add ${1}/30 dev ${ns}
  135        ${ip} link set ${ns} up
  136        #${ip} route add ${1} dev ${ns}
  137 
  138        ${ip} -n ${ns} addr add ${2}/30 dev ${ns}_peer broadcast +
  139        ${ip} -n ${ns} link set ${ns}_peer up
  140        ${ip} -n ${ns} route add ${1} dev ${ns}_peer
  141 
  142        iptables_rule filter INPUT -A -i ${ns} \
  143           -p tcp --dport ${p_domain} -j ${ACC}
  144        iptables_rule filter INPUT -A -i ${ns} \
  145           -p udp --dport ${p_domain} -j ${ACC}
  146        iptables_rule filter INPUT -A -i ${ns} -j REJECT
  147 
  148   So now only DNS is allowed from the network namespace to the base namespace,
  149   where dnsmasq is listening on "interface NSNAME" (maybe after a restart).
  150   All other traffic generated in NSNAME but to 10.4.0.9 is routed through the
  151   WireGuard VPN.
  152 
  153 A watchdog
  154 
  155   Driven by cron one can selectively whitelist endpoints without fixed
  156   IP addresses, in order to apply strict black listing on those
  157 
  158     # iptables -A INPUT -p udp --dport PORT -j ACCEPT
  159 
  160   rules that are needed (on servers).  Here is one idea, it is pretty fresh
  161   but working for some time here.  Imagine a configuration
  162 
  163     : ${RUNDIR:=/run}
  164 
  165     # (y/empty) Wireguard VPN (ie: look for WG_digit_ADDR settings)?
  166     : ${WG:=}
  167        #     If empty
  168        : ${WG_WATCHDOG:=${RUNDIR}/.net-qos-wg-watch}
  169        #  For wg_watchdog() (aka "$0 watchdog-wg"): persistance data file.
  170        #  Watchdog only works if non-empty.
  171        # -> WG_digit_ADDR='any wg(8) address:LISTEN-PORT'
  172        #     Ie address+CIDR netmask plus listen port.  Whether we create it.
  173     ....
  174 
  175   In the following, please substitute ACCEPT for f_m1, and INPUT for i_good.
  176   I currently use primitive CONNMARK jumps for all my traffic, instead of
  177   using the "fwmark" feature of WireGuard to shortcut that for the VPN.
  178 
  179 wg_watchdog() {
  180    [ -n "${WG_WATCHDOG}" ] || {
  181       echo >&2 '$WG_WATCHDOG is not set'
  182       return 0
  183    }
  184 
  185    touch "${WG_WATCHDOG}" "${WG_WATCHDOG}".new "${WG_WATCHDOG}".lck
  186    chown root:root "${WG_WATCHDOG}" "${WG_WATCHDOG}".new "${WG_WATCHDOG}".lck
  187    chmod 0600 "${WG_WATCHDOG}" "${WG_WATCHDOG}".new "${WG_WATCHDOG}".lck
  188 
  189    if exec 7>"${WG_WATCHDOG}.lck" && flock 7; then :; else
  190       echo >&2 'Cannot aquire lock file '${WG_WATCHDOG}.lck
  191       return 1
  192    fi
  193 
  194    # New list of peers
  195    printf '' > "${WG_WATCHDOG}".new
  196    wl=
  197 
  198    id=0
  199    while :; do
  200       eval x=\$WG_${id}_ADDR
  201       [ -z "${x}" ] && break
  202 
  203       wg__splita "${x}"
  204       dport=${port}
  205 
  206       x=`${wg} show wg${id} endpoints 2>/dev/null`
  207       if [ ${?} -eq 0 ]; then
  208          x=`echo ${x} | cut -f2 -d' '`
  209          if [ "${x}" != '(none)' ]; then
  210             wg__splita "${x}"
  211             wl=${wl}' '${addr}
  212             printf -- "-p udp --src %s --dport %s -j f_m1\n" \
  213                "${addr}" "${dport}" >> "${WG_WATCHDOG}".new
  214             #--sport ${port}
  215          fi
  216       fi
  217 
  218       id=$((id + 1))
  219    done
  220 
  221    # ..if different to old one, recreate firewall rules
  222    if cmp "${WG_WATCHDOG}".new "${WG_WATCHDOG}" >/dev/null 2>&1; then :; else
  223       if [ -s "${WG_WATCHDOG}" ]; then
  224          while read l; do
  225             iptables_rule filter i_good -D ${l}
  226          done < "${WG_WATCHDOG}"
  227       fi
  228 
  229       # Add new list of peers
  230       if [ -n "${wl}" ]; then
  231          while read l; do
  232             iptables_rule filter i_good -I 1 ${l}
  233          done < "${WG_WATCHDOG}".new
  234          logger -t /root/bin/net-qos.sh/WG 'whitelist: '${wl}
  235       fi
  236 
  237       cp -f "${WG_WATCHDOG}".new "${WG_WATCHDOG}"
  238    fi
  239 
  240    exec 7>&-
  241 }
  242 
  243 wg__splita() {
  244    addr=${1%:*}
  245    port=${1##*:}
  246    ip6=0
  247 
  248    if [ "${addr}" != "${addr%]*}" ]; then
  249       ip6=1
  250       addr=${addr%]*}
  251       addr=${addr#[*}
  252    fi
  253 
  254    if [ "${addr}" != "${addr%/*}" ]; then
  255       mask=/${addr#*/}
  256       addr=${addr%/*}
  257    else
  258       mask=/32
  259    fi
  260 }
  261 
  262 # s-ts-mode

Generated by cgit