diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cd56d447 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bindata.go diff --git a/README.md b/README.md index 157caf3f..8cc6becf 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Running Subspace on a VPS is designed to be as simple as possible. **Recommended Specs** * Type: VPS or dedicated -* Distribution: Ubuntu 16.04 (Xenial) +* Distribution: Ubuntu 16.04 (Xenial) or Ubuntu 18.04 (Bionic) * Memory: 512MB or greater ### 2. Add a DNS record @@ -63,7 +63,7 @@ Subspace runs a TLS ("SSL") https server on port 443/tcp. It also runs a standar **Requirements** * Your server must have a publicly resolvable DNS record. -* Your server must be reachable over the internet on ports 80/tcp and 443/tcp and 51820/udp (WireGuard). +* Your server must be reachable over the internet on ports 80/tcp, 443/tcp and 51820/udp (Default WireGuard port, user changeable). ### Usage @@ -138,7 +138,19 @@ docker create \ --cap-add NET_ADMIN \ --volume /usr/bin/wg:/usr/bin/wg \ --volume /data:/data \ - --env SUBSPACE_HTTP_HOST=subspace.example.com \ + --env SUBSPACE_HTTP_HOST="subspace.example.com" \ + # Optional variable to change upstream DNS provider + --env SUBSPACE_NAMESERVER="1.1.1.1" \ + # Optional variable to change WireGuard Listenport + --env SUBSPACE_LISTENPORT="51820" \ + # Optional variables to change IPv4/v6 prefixes + --env SUBSPACE_IPV4_POOL="10.99.97.0/24" \ + --env SUBSPACE_IPV6_POOL="fd00::10:97:0/64" \ + # Optional variables to change IPv4/v6 Gateway + --env SUBSPACE_IPV4_GW="10.99.97.1" \ + --env SUBSPACE_IPV6_GW="fd00::10:97:1" \ + # Optional variable to enable or disable IPv6 NAT + --env SUBSPACE_IPV6_NAT_ENABLED=1 \ subspacecloud/subspace:latest $ sudo docker start subspace @@ -149,6 +161,30 @@ $ sudo docker logs subspace ``` +#### Docker-Compose Example + +``` +version: "3.3" +services: + subspace: + image: subspace/subspace:latest + container_name: subspace + volumes: + - /usr/bin/wg:/usr/bin/wg + - /opt/docker/subspace:/data + restart: always + environment: + - SUBSPACE_HTTP_HOST=subspace.example.org + - SUBSPACE_LETSENCRYPT=true + - SUBSPACE_HTTP_INSECURE=false + - SUBSPACE_HTTP_ADDR=":80" + - SUBSPACE_NAMESERVER=1.1.1.1 + - SUBSPACE_LISTENPORT=51820 + cap_add: + - NET_ADMIN + network_mode: "host" +``` + #### Updating the container image Pull the latest image, remove the container, and re-create the container as explained above. diff --git a/entrypoint.sh b/entrypoint.sh index 259625a6..bf60ba5d 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -9,12 +9,21 @@ if [ -z "${SUBSPACE_HTTP_HOST-}" ] ; then echo "Environment variable SUBSPACE_HTTP_HOST required. Exiting." exit 1 fi - # Optional environment variables. if [ -z "${SUBSPACE_BACKLINK-}" ] ; then export SUBSPACE_BACKLINK="" fi +if [ -z "${SUBSPACE_IPV4_POOL-}" ] ; then + export SUBSPACE_IPV4_POOL="10.99.97.0/24" +fi +if [ -z "${SUBSPACE_IPV6_POOL-}" ] ; then + export SUBSPACE_IPV6_POOL="fd00::10:97:0/112" +fi +if [ -z "${SUBSPACE_NAMESERVER-}" ] ; then + export SUBSPACE_NAMESERVER="1.1.1.1" +fi + if [ -z "${SUBSPACE_LETSENCRYPT-}" ] ; then export SUBSPACE_LETSENCRYPT="true" fi @@ -23,75 +32,82 @@ if [ -z "${SUBSPACE_HTTP_ADDR-}" ] ; then export SUBSPACE_HTTP_ADDR=":80" fi +if [ -z "${SUBSPACE_LISTENPORT-}" ] ; then + export SUBSPACE_LISTENPORT="51820" +fi + if [ -z "${SUBSPACE_HTTP_INSECURE-}" ] ; then export SUBSPACE_HTTP_INSECURE="false" fi -export NAMESERVER="1.1.1.1" export DEBIAN_FRONTEND="noninteractive" +if [ -z "${SUBSPACE_IPV4_GW-}" ] ; then + export SUBSPACE_IPV4_PREF=$(echo ${SUBSPACE_IPV4_POOL-} | cut -d '/' -f1 |sed 's/.0$/./g' ) + export SUBSPACE_IPV4_GW=$(echo ${SUBSPACE_IPV4_PREF-}1) + +fi +if [ -z "${SUBSPACE_IPV6_GW-}" ] ; then + export SUBSPACE_IPV6_PREF=$(echo ${SUBSPACE_IPV6_POOL-} | cut -d '/' -f1 |sed 's/:0$/:/g' ) + export SUBSPACE_IPV6_GW=$(echo ${SUBSPACE_IPV6_PREF-}1) +fi + +if [ -z "${SUBSPACE_IPV6_NAT_ENABLED-}" ] ; then + export SUBSPACE_IPV6_NAT_ENABLED=1 +fi + # Set DNS server -echo "nameserver ${NAMESERVER}" >/etc/resolv.conf +echo "nameserver ${SUBSPACE_NAMESERVER}" >/etc/resolv.conf # ipv4 -if ! /sbin/iptables -t nat --check POSTROUTING -s 10.99.97.0/24 -j MASQUERADE ; then - /sbin/iptables -t nat --append POSTROUTING -s 10.99.97.0/24 -j MASQUERADE +if ! /sbin/iptables -t nat --check POSTROUTING -s ${SUBSPACE_IPV4_POOL} -j MASQUERADE ; then + /sbin/iptables -t nat --append POSTROUTING -s ${SUBSPACE_IPV4_POOL} -j MASQUERADE fi if ! /sbin/iptables --check FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT ; then /sbin/iptables --append FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT fi -if ! /sbin/iptables --check FORWARD -s 10.99.97.0/24 -j ACCEPT ; then - /sbin/iptables --append FORWARD -s 10.99.97.0/24 -j ACCEPT +if ! /sbin/iptables --check FORWARD -s ${SUBSPACE_IPV4_POOL} -j ACCEPT ; then + /sbin/iptables --append FORWARD -s ${SUBSPACE_IPV4_POOL} -j ACCEPT fi +if [[ ${SUBSPACE_IPV6_NAT_ENABLED-} -gt 0 ]]; then # ipv6 -if ! /sbin/ip6tables -t nat --check POSTROUTING -s fd00::10:97:0/112 -j MASQUERADE ; then - /sbin/ip6tables -t nat --append POSTROUTING -s fd00::10:97:0/112 -j MASQUERADE -fi - -if ! /sbin/ip6tables --check FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT ; then - /sbin/ip6tables --append FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -fi - -if ! /sbin/ip6tables --check FORWARD -s fd00::10:97:0/112 -j ACCEPT ; then - /sbin/ip6tables --append FORWARD -s fd00::10:97:0/112 -j ACCEPT + if ! /sbin/ip6tables -t nat --check POSTROUTING -s ${SUBSPACE_IPV6_POOL} -j MASQUERADE ; then + /sbin/ip6tables -t nat --append POSTROUTING -s ${SUBSPACE_IPV6_POOL} -j MASQUERADE + fi + + if ! /sbin/ip6tables --check FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT ; then + /sbin/ip6tables --append FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + fi + + if ! /sbin/ip6tables --check FORWARD -s ${SUBSPACE_IPV6_POOL} -j ACCEPT ; then + /sbin/ip6tables --append FORWARD -s ${SUBSPACE_IPV6_POOL} -j ACCEPT + fi fi # ipv4 - DNS Leak Protection -if ! /sbin/iptables -t nat --check OUTPUT -s 10.99.97.0/16 -p udp --dport 53 -j DNAT --to 10.99.97.1:53 ; then - /sbin/iptables -t nat --append OUTPUT -s 10.99.97.0/16 -p udp --dport 53 -j DNAT --to 10.99.97.1:53 +if ! /sbin/iptables -t nat --check OUTPUT -s ${SUBSPACE_IPV4_POOL} -p udp --dport 53 -j DNAT --to ${SUBSPACE_IPV4_GW}:53 ; then + /sbin/iptables -t nat --append OUTPUT -s ${SUBSPACE_IPV4_POOL} -p udp --dport 53 -j DNAT --to ${SUBSPACE_IPV4_GW}:53 fi -if ! /sbin/iptables -t nat --check OUTPUT -s 10.99.97.0/16 -p tcp --dport 53 -j DNAT --to 10.99.97.1:53 ; then - /sbin/iptables -t nat --append OUTPUT -s 10.99.97.0/16 -p tcp --dport 53 -j DNAT --to 10.99.97.1:53 +if ! /sbin/iptables -t nat --check OUTPUT -s ${SUBSPACE_IPV4_POOL} -p tcp --dport 53 -j DNAT --to ${SUBSPACE_IPV4_GW}:53 ; then + /sbin/iptables -t nat --append OUTPUT -s ${SUBSPACE_IPV4_POOL} -p tcp --dport 53 -j DNAT --to ${SUBSPACE_IPV4_GW}:53 fi # ipv6 - DNS Leak Protection -if ! /sbin/ip6tables --wait -t nat --check OUTPUT -s fd00::10:97:0/112 -p udp --dport 53 -j DNAT --to fd00::10:97:1 ; then - /sbin/ip6tables --wait -t nat --append OUTPUT -s fd00::10:97:0/112 -p udp --dport 53 -j DNAT --to fd00::10:97:1 +if ! /sbin/ip6tables --wait -t nat --check OUTPUT -s ${SUBSPACE_IPV6_POOL} -p udp --dport 53 -j DNAT --to ${SUBSPACE_IPV6_GW} ; then + /sbin/ip6tables --wait -t nat --append OUTPUT -s ${SUBSPACE_IPV6_POOL} -p udp --dport 53 -j DNAT --to ${SUBSPACE_IPV6_GW} fi -if ! /sbin/ip6tables --wait -t nat --check OUTPUT -s fd00::10:97:0/112 -p tcp --dport 53 -j DNAT --to fd00::10:97:1 ; then - /sbin/ip6tables --wait -t nat --append OUTPUT -s fd00::10:97:0/112 -p tcp --dport 53 -j DNAT --to fd00::10:97:1 +if ! /sbin/ip6tables --wait -t nat --check OUTPUT -s ${SUBSPACE_IPV6_POOL} -p tcp --dport 53 -j DNAT --to ${SUBSPACE_IPV6_GW} ; then + /sbin/ip6tables --wait -t nat --append OUTPUT -s ${SUBSPACE_IPV6_POOL} -p tcp --dport 53 -j DNAT --to ${SUBSPACE_IPV6_GW} fi -# # Delete -# /sbin/iptables -t nat --delete OUTPUT -s 10.99.97.0/16 -p udp --dport 53 -j DNAT --to 10.99.97.1:53 -# /sbin/iptables -t nat --delete OUTPUT -s 10.99.97.0/16 -p tcp --dport 53 -j DNAT --to 10.99.97.1:53 -# /sbin/ip6tables --wait -t nat --delete OUTPUT -s fd00::10:97:0/112 -p udp --dport 53 -j DNAT --to fd00::10:97:1 -# /sbin/ip6tables --wait -t nat --delete OUTPUT -s fd00::10:97:0/112 -p tcp --dport 53 -j DNAT --to fd00::10:97:1 -# /sbin/iptables -t nat --delete POSTROUTING -s 10.99.97.0/24 -j MASQUERADE -# /sbin/iptables --delete FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -# /sbin/iptables --delete FORWARD -s 10.99.97.0/24 -j ACCEPT -# /sbin/ip6tables -t nat --delete POSTROUTING -s fd00::10:97:0/112 -j MASQUERADE -# /sbin/ip6tables --delete FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -# /sbin/ip6tables --delete FORWARD -s fd00::10:97:0/112 -j ACCEPT - # -# WireGuard (10.99.97.0/24) +# WireGuard (${SUBSPACE_IPV4_POOL}) # if ! test -d /data/wireguard ; then mkdir /data/wireguard @@ -109,7 +125,7 @@ fi cat </data/wireguard/server.conf [Interface] PrivateKey = $(cat /data/wireguard/server.private) -ListenPort = 51820 +ListenPort = ${SUBSPACE_LISTENPORT} WGSERVER cat /data/wireguard/peers/*.conf >>/data/wireguard/server.conf @@ -118,16 +134,19 @@ if ip link show wg0 2>/dev/null; then ip link del wg0 fi ip link add wg0 type wireguard -ip addr add 10.99.97.1/24 dev wg0 -ip addr add fd00::10:97:1/112 dev wg0 +export SUBSPACE_IPV4_CIDR=$(echo ${SUBSPACE_IPV4_POOL-} |cut -d '/' -f2) +ip addr add ${SUBSPACE_IPV4_GW}/${SUBSPACE_IPV4_CIDR} dev wg0 +export SUBSPACE_IPV6_CIDR=$(echo ${SUBSPACE_IPV6_POOL-} |cut -d '/' -f2) +ip addr add ${SUBSPACE_IPV6_GW}/${SUBSPACE_IPV6_CIDR} dev wg0 wg setconf wg0 /data/wireguard/server.conf ip link set wg0 up + # dnsmasq service if ! test -d /etc/sv/dnsmasq ; then cat </etc/dnsmasq.conf # Only listen on necessary addresses. - listen-address=127.0.0.1,10.99.97.1,fd00::10:97:1 + listen-address=127.0.0.1,${SUBSPACE_IPV4_GW},${SUBSPACE_IPV6_GW} # Never forward plain names (without a dot or domain part) domain-needed diff --git a/handlers.go b/handlers.go index b2466a4f..2997aa7b 100644 --- a/handlers.go +++ b/handlers.go @@ -1,6 +1,7 @@ package main import ( + "os" "fmt" "io/ioutil" "net/http" @@ -21,6 +22,13 @@ var ( maxProfilesPerUser = 10 ) +func getEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} + func ssoHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { if token := samlSP.GetAuthorizationToken(r); token != nil { http.Redirect(w, r, "/", http.StatusFound) @@ -365,43 +373,89 @@ func profileAddHandler(w *Web) { return } + ipv4Pref := "10.99.97." + if pref := getEnv("SUBSPACE_IPV4_PREF", "nil"); pref != "nil" { + ipv4Pref = pref + } + ipv4Gw := "10.99.97.1" + if gw := getEnv("SUBSPACE_IPV4_GW", "nil"); gw != "nil" { + ipv4Gw = gw + } + ipv4Cidr := "24" + if cidr := getEnv("SUBSPACE_IPV4_CIDR", "nil"); cidr != "nil" { + ipv4Cidr = cidr + } + + ipv6Pref := "fd00::10:97:" + if pref := getEnv("SUBSPACE_IPV6_PREF", "nil"); pref != "nil" { + ipv6Pref = pref + } + ipv6Gw := "fd00::10:97:1" + if gw := getEnv("SUBSPACE_IPV6_GW", "nil"); gw != "nil" { + ipv6Gw = gw + } + ipv6Cidr := "64" + if cidr := getEnv("SUBSPACE_IPV6_CIDR", "nil"); cidr != "nil" { + ipv6Cidr = cidr + } + listenport := "51820" + if port := getEnv("SUBSPACE_LISTENPORT", "nil"); port != "nil" { + listenport = port + } + script := ` cd {{$.Datadir}}/wireguard wg_private_key="$(wg genkey)" wg_public_key="$(echo $wg_private_key | wg pubkey)" -wg set wg0 peer ${wg_public_key} allowed-ips 10.99.97.{{$.Profile.Number}}/32,fd00::10:97:{{$.Profile.Number}}/128 +wg set wg0 peer ${wg_public_key} allowed-ips {{$.IPv4Pref}}{{$.Profile.Number}}/32,{{$.IPv6Pref}}{{$.Profile.Number}}/128 cat <peers/{{$.Profile.ID}}.conf [Peer] PublicKey = ${wg_public_key} -AllowedIPs = 10.99.97.{{$.Profile.Number}}/32,fd00::10:97:{{$.Profile.Number}}/128 - +AllowedIPs = {{$.IPv4Pref}}{{$.Profile.Number}}/32,{{$.IPv6Pref}}{{$.Profile.Number}}/128 WGPEER - cat <clients/{{$.Profile.ID}}.conf +cat <clients/{{$.Profile.ID}}.conf [Interface] PrivateKey = ${wg_private_key} -DNS = 10.99.97.1, fd00::10:97:1 -Address = 10.99.97.{{$.Profile.Number}}/22,fd00::10:97:{{$.Profile.Number}}/112 +DNS = {{$.IPv4Gw}}, {{$.IPv6Gw}} +Address = {{$.IPv4Pref}}{{$.Profile.Number}}/{{$.IPv4Cidr}},{{$.IPv6Pref}}{{$.Profile.Number}}/{{$.IPv6Cidr}} [Peer] PublicKey = $(cat server.public) -Endpoint = {{$.Domain}}:51820 +Endpoint = {{$.Domain}}:{{$.Listenport}} AllowedIPs = 0.0.0.0/0, ::/0 WGCLIENT ` _, err = bash(script, struct { - Datadir string - Profile Profile - Domain string + Profile Profile + Domain string + Datadir string + IPv4Gw string + IPv6Gw string + IPv4Pref string + IPv6Pref string + IPv4Cidr string + IPv6Cidr string + Listenport string }{ - datadir, profile, httpHost, + datadir, + ipv4Gw, + ipv6Gw, + ipv4Pref, + ipv6Pref, + ipv4Cidr, + ipv6Cidr, + listenport, }) if err != nil { logger.Warn(err) + f, _ := os.Create("/tmp/error.txt") + errstr := fmt.Sprintln(err) + f.WriteString(errstr) w.Redirect("/?error=addprofile") return } diff --git a/subspace-linux-amd64 b/subspace-linux-amd64 index 358bae55..ca80df75 100755 Binary files a/subspace-linux-amd64 and b/subspace-linux-amd64 differ