summaryrefslogtreecommitdiff
path: root/obfsproxy
diff options
context:
space:
mode:
Diffstat (limited to 'obfsproxy')
-rw-r--r--obfsproxy/Makefile18
-rwxr-xr-xobfsproxy/leap-vpn.sh13
-rw-r--r--obfsproxy/main.go154
-rw-r--r--obfsproxy/main_test.go8
4 files changed, 155 insertions, 38 deletions
diff --git a/obfsproxy/Makefile b/obfsproxy/Makefile
new file mode 100644
index 0000000..8694349
--- /dev/null
+++ b/obfsproxy/Makefile
@@ -0,0 +1,18 @@
+RHOST=163.172.126.44:443
+LHOST="10.0.0.209:443"
+
+run:
+ sudo ./obfsproxy -addr ${LHOST} -vpn ${RHOST} -state test_data -c test_data/obfs4.json
+
+certs:
+ curl -k https://black.riseup.net/ca.crt > /tmp/ca.crt
+ curl -k https://api.black.riseup.net/3/cert > /tmp/cert.pem
+
+check:
+ curl https://wtfismyip.com/json
+
+stop:
+ pkill -9 obfsproxy
+
+obfsproxy:
+ go build
diff --git a/obfsproxy/leap-vpn.sh b/obfsproxy/leap-vpn.sh
new file mode 100755
index 0000000..4fb09c3
--- /dev/null
+++ b/obfsproxy/leap-vpn.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+sudo openvpn \
+ --verb 3 \
+ --tls-cipher DHE-RSA-AES128-SHA \
+ --cipher AES-128-CBC \
+ --dev tun --client --tls-client \
+ --remote-cert-tls server --tls-version-min 1.2 \
+ --ca /tmp/ca.crt --cert /tmp/cert.pem --key /tmp/cert.pem \
+ --proto tcp4 \
+ --remote localhost 4430 \
+ --socks-proxy localhost 4430 \
+ --route $GW 255.255.255.255 net_gateway \
+ --persist-tun
diff --git a/obfsproxy/main.go b/obfsproxy/main.go
index a35a26d..5e56789 100644
--- a/obfsproxy/main.go
+++ b/obfsproxy/main.go
@@ -4,7 +4,9 @@ package main
import (
"context"
"encoding/json"
+ "errors"
"flag"
+ "fmt"
"io"
"log"
"net"
@@ -12,12 +14,19 @@ import (
"os/signal"
"0xacab.org/leap/obfsvpn"
-
"git.torproject.org/pluggable-transports/goptlib.git"
)
const transportName = "obfs4"
+type Config struct {
+ NodeID string `json:"node-id"`
+ PrivateKey string `json:"private-key"`
+ PublicKey string `json:"public-key"`
+ DRBGSeed string `json:"drbg-seed"`
+ IatMode int `json:"iat-mode"`
+}
+
func main() {
// Setup logging.
logger := log.New(os.Stderr, "", log.LstdFlags)
@@ -48,15 +57,17 @@ func main() {
}
tcpVPNAddr, err := net.ResolveTCPAddr("tcp", vpnAddr)
+ log.Println("target:", tcpVPNAddr)
if err != nil {
logger.Fatalf("error resolving VPN address: %v", err)
}
+ var cfg Config
fd, err := os.Open(cfgFile)
+ log.Println("opening:", cfgFile)
if err != nil {
logger.Fatalf("error opening config file: %v", err)
}
- var cfg Config
err = json.NewDecoder(fd).Decode(&cfg)
if err != nil {
logger.Fatalf("error decoding config: %v", err)
@@ -67,15 +78,16 @@ func main() {
debug.SetOutput(os.Stderr)
}
+ log.Println("config:", cfg)
+
// Setup graceful shutdown.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
- listenConfig := obfsvpn.ListenConfig{
- NodeID: cfg.NodeID,
- PrivateKey: cfg.PrivateKey,
- Seed: cfg.DRBGSeed,
- StateDir: stateDir,
+ listenConfig, err := obfsvpn.NewListenConfig(cfg.NodeID, cfg.PrivateKey, cfg.PublicKey, cfg.DRBGSeed, stateDir)
+ if err != nil {
+ logger.Fatalf("error creating listener from config: %v", err)
}
+ log.Println("DEBUG:", listenConfig)
ln, err := listenConfig.Listen(ctx, "tcp", addr)
if err != nil {
logger.Fatalf("error binding to %s: %v", addr, err)
@@ -105,44 +117,122 @@ func main() {
return
}
debug.Printf("accepted connection %v…", conn)
- go proxyConn(ctx, info, conn, net.Dialer{}, logger, debug)
+ go proxyConn(ctx, info, conn, logger, debug)
}
}
-func proxyConn(ctx context.Context, info *pt.ServerInfo, conn net.Conn, d net.Dialer, logger, debug *log.Logger) {
+// obfsConn is a connection to the obfs4 client that we have accepted. we will dial to the remote contained in info
+func proxyConn(ctx context.Context, info *pt.ServerInfo, obfsConn net.Conn, logger, debug *log.Logger) {
defer func() {
- err := conn.Close()
+ err := obfsConn.Close()
if err != nil {
debug.Printf("error closing connection: %v", err)
}
}()
- // TODO: do we actually want to send the USERADDR/TRANSPORT ExtOrPort
- // commands? I don't understand how this works, so I'm unsure.
- remote, err := pt.DialOr(info, conn.RemoteAddr().String(), transportName)
+ // FIXME scrub ips in other than debug mode!
+ log.Println("Dialing:", info.OrAddr)
+ log.Println("Obfs4 client:", obfsConn.RemoteAddr().String())
+
+ /*
+ in the case of Tor, pt.DialOr returns a *net.TCPConn after dialing info.OrAddr.
+ in the vpn case (or any transparent proxy really), we do use
+ the pt.DialOr method to simply get a dialer to our upstream VPN remote.
+
+ keeping this terminology is a bit stupid and slightly confusing, instead
+ we could get the clearConn just by doing:
+
+ s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ that is precisely what the code in ptlib is doing.
+
+ We also aspire at being a generic PT at some point, so perhaps it's better to keep the usage
+ of DialOr?
+
+ Maybe not, and so we don't need to keep the confusing info struct.
+ */
+
+ // we'll refer to the connection to the usptream node as "clearConn", as opposed to the obfuscated conn.
+ // but for sure openvpn or whatever protocol you wrap has its own layer of encryption :)
+
+ clearConn, err := pt.DialOr(info, obfsConn.RemoteAddr().String(), transportName)
+
if err != nil {
logger.Printf("error dialing remote: %v", err)
return
}
- defer func() {
- err := remote.Close()
- if err != nil {
- debug.Printf("error closing remote connection: %v", err)
- }
- }()
- go func() {
- _, err := io.Copy(remote, conn)
- if err != nil {
- logger.Printf("error proxying client data to remote: %v", err)
- return
- }
- }()
- go func() {
- _, err := io.Copy(conn, remote)
- if err != nil {
- logger.Printf("error proxying remote data to client: %v", err)
- return
+ if err = CopyLoop(clearConn, obfsConn); err != nil {
+ debug.Printf("%s - closed connection: %s", "obfsvpn", err.Error())
+ } else {
+ debug.Printf("%s - closed connection", "obfsvpn")
+ }
+}
+
+// a stock copy loop. let's not dwell too much on who's client and who's server
+
+func CopyLoop(left net.Conn, right net.Conn) error {
+
+ fmt.Println("--> Entering copy loop.")
+
+ if left == nil {
+ fmt.Fprintln(os.Stderr, "--> Copy loop has a nil connection (left).")
+ return errors.New("copy loop has a nil connection (left)")
+ }
+
+ if right == nil {
+ fmt.Fprintln(os.Stderr, "--> Copy loop has a nil connection (right).")
+ return errors.New("copy loop has a nil connection (right)")
+ }
+
+ // Note: right is always the pt connection.
+ lockL := make(chan bool)
+ lockR := make(chan bool)
+ errChan := make(chan error)
+
+ go CopyLeftToRight(left, right, lockL, errChan)
+ go CopyRightToLeft(left, right, lockR, errChan)
+
+ leftUp := true
+ rightUp := true
+
+ var copyErr error
+
+ for leftUp || rightUp {
+ select {
+ case <-lockL:
+ leftUp = false
+ case <-lockR:
+ rightUp = false
+ case copyErr = <-errChan:
+ log.Println("Error while copying")
}
- }()
+ }
+
+ // XXX better to defer?
+ left.Close()
+ right.Close()
+
+ return copyErr
+}
+
+// TODO check for data races
+
+func CopyLeftToRight(l net.Conn, r net.Conn, ll chan bool, errChan chan error) {
+ _, e := io.Copy(r, l)
+ ll <- true
+ if e != nil {
+ errChan <- e
+ }
+}
+
+func CopyRightToLeft(l net.Conn, r net.Conn, lr chan bool, errChan chan error) {
+ _, e := io.Copy(l, r)
+ lr <- true
+ if e != nil {
+ errChan <- e
+ }
}
diff --git a/obfsproxy/main_test.go b/obfsproxy/main_test.go
index d22d3af..cdead61 100644
--- a/obfsproxy/main_test.go
+++ b/obfsproxy/main_test.go
@@ -1,15 +1,9 @@
package main
import (
- "context"
"flag"
- "net"
"os"
- "os/exec"
"testing"
- "time"
-
- "golang.org/x/net/proxy"
)
type testWriter struct {
@@ -33,6 +27,7 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
+/*
func TestRoundTrip(t *testing.T) {
// Setup and exec the proxy:
ctx, cancel := context.WithCancel(context.Background())
@@ -90,3 +85,4 @@ func TestRoundTrip(t *testing.T) {
select {}
}
+*/