Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bindata.go
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down
103 changes: 61 additions & 42 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -109,7 +125,7 @@ fi
cat <<WGSERVER >/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
Expand All @@ -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 <<DNSMASQ >/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
Expand Down
76 changes: 65 additions & 11 deletions handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"os"
"fmt"
"io/ioutil"
"net/http"
Expand All @@ -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)
Expand Down Expand Up @@ -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 <<WGPEER >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 <<WGCLIENT >clients/{{$.Profile.ID}}.conf
cat <<WGCLIENT >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
}
Expand Down
Binary file modified subspace-linux-amd64
Binary file not shown.