summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-12-23 00:43:29 +0100
committerkali kaneko (leap communications) <kali@leap.se>2021-12-23 00:43:40 +0100
commitbddadc7323d6467f5233f26b97652fe671d77eed (patch)
tree7f5aba2a33e852a2be04ff3e6bbd0383376d91f1
parentd83fd91d6293386867cc908f05b5f3f4d95a7053 (diff)
[ui] expose bonafide+snowflake bootstrap events
-rw-r--r--pkg/backend/api.go1
-rw-r--r--pkg/backend/status.go82
-rw-r--r--pkg/bitmask/bitmask.go8
-rw-r--r--pkg/snowflake/bootstrap.go73
-rw-r--r--pkg/vpn/bonafide/bonafide.go24
-rw-r--r--pkg/vpn/bonafide/eip_service.go12
-rw-r--r--pkg/vpn/bonafide/gateways.go13
-rw-r--r--pkg/vpn/main.go7
-rw-r--r--pkg/vpn/openvpn.go1
9 files changed, 166 insertions, 55 deletions
diff --git a/pkg/backend/api.go b/pkg/backend/api.go
index ba07adf..02aa383 100644
--- a/pkg/backend/api.go
+++ b/pkg/backend/api.go
@@ -37,6 +37,7 @@ func Login(username, password string) {
ctx.LoginDialog = true
ctx.Errors = "bad_auth"
}
+ // XXX shouldn't this be statusChanged?
go ctx.updateStatus()
}
diff --git a/pkg/backend/status.go b/pkg/backend/status.go
index de6364f..f6b195c 100644
--- a/pkg/backend/status.go
+++ b/pkg/backend/status.go
@@ -33,36 +33,38 @@ var updateMutex sync.Mutex
// them.
type connectionCtx struct {
- AppName string `json:"appName"`
- Provider string `json:"provider"`
- TosURL string `json:"tosURL"`
- HelpURL string `json:"helpURL"`
- AskForDonations bool `json:"askForDonations"`
- DonateDialog bool `json:"donateDialog"`
- DonateURL string `json:"donateURL"`
- LoginDialog bool `json:"loginDialog"`
- LoginOk bool `json:"loginOk"`
- Version string `json:"version"`
- Errors string `json:"errors"`
- Status status `json:"status"`
- Locations map[string]float64 `json:"locations"`
- LocationLabels map[string][]string `json:"locationLabels"`
- CurrentGateway string `json:"currentGateway"`
- CurrentLocation string `json:"currentLocation"`
- CurrentCountry string `json:"currentCountry"`
- BestLocation string `json:"bestLocation"`
- Transport string `json:"transport"`
- UseUDP bool `json:"udp"`
- OffersUDP bool `json:"offersUdp"`
- ManualLocation bool `json:"manualLocation"`
- IsReady bool `json:"isReady"`
- CanUpgrade bool `json:"canUpgrade"`
- Motd string `json:"motd"`
- HasTor bool `json:"hasTor"`
- UseSnowflake bool `json:"snowflake"`
- bm bitmask.Bitmask
- autostart bitmask.Autostart
- cfg *config.Config
+ AppName string `json:"appName"`
+ Provider string `json:"provider"`
+ TosURL string `json:"tosURL"`
+ HelpURL string `json:"helpURL"`
+ AskForDonations bool `json:"askForDonations"`
+ DonateDialog bool `json:"donateDialog"`
+ DonateURL string `json:"donateURL"`
+ LoginDialog bool `json:"loginDialog"`
+ LoginOk bool `json:"loginOk"`
+ Version string `json:"version"`
+ Errors string `json:"errors"`
+ Status status `json:"status"`
+ Locations map[string]float64 `json:"locations"`
+ LocationLabels map[string][]string `json:"locationLabels"`
+ CurrentGateway string `json:"currentGateway"`
+ CurrentLocation string `json:"currentLocation"`
+ CurrentCountry string `json:"currentCountry"`
+ BestLocation string `json:"bestLocation"`
+ Transport string `json:"transport"`
+ UseUDP bool `json:"udp"`
+ OffersUDP bool `json:"offersUdp"`
+ ManualLocation bool `json:"manualLocation"`
+ IsReady bool `json:"isReady"`
+ CanUpgrade bool `json:"canUpgrade"`
+ Motd string `json:"motd"`
+ HasTor bool `json:"hasTor"`
+ UseSnowflake bool `json:"snowflake"`
+ SnowflakeProgress int `json:"snowflakeProgress"`
+ SnowflakeTag string `json:"snowflakeTag"`
+ bm bitmask.Bitmask
+ autostart bitmask.Autostart
+ cfg *config.Config
}
func (c *connectionCtx) toJson() ([]byte, error) {
@@ -78,7 +80,7 @@ func (c *connectionCtx) toJson() ([]byte, error) {
c.Transport = transport
c.UseUDP = c.cfg.UDP // TODO initialize bitmask param?
c.OffersUDP = c.bm.OffersUDP()
- c.UseSnowflake = c.cfg.Snowflake // TODO initialize bitmask param?
+ c.UseSnowflake = c.cfg.Snowflake // TODO initialize bitmask
c.ManualLocation = c.bm.IsManualLocation()
c.CanUpgrade = c.bm.CanUpgrade()
c.Motd = c.bm.GetMotd()
@@ -102,6 +104,16 @@ func (c connectionCtx) updateStatus() {
setStatusFromStr(stStr)
}
+ go func() {
+ snowflakeCh := c.bm.GetSnowflakeCh()
+ for {
+ select {
+ case event := <-snowflakeCh:
+ setSnowflakeStatus(event)
+ }
+ }
+ }()
+
statusCh := c.bm.GetStatusCh()
for {
select {
@@ -111,6 +123,14 @@ func (c connectionCtx) updateStatus() {
}
}
+func setSnowflakeStatus(event *snowflake.StatusEvent) {
+ statusMutex.Lock()
+ defer statusMutex.Unlock()
+ ctx.SnowflakeProgress = event.Progress
+ ctx.SnowflakeTag = event.Tag
+ go trigger(OnStatusChanged)
+}
+
func setStatus(st status) {
statusMutex.Lock()
defer statusMutex.Unlock()
diff --git a/pkg/bitmask/bitmask.go b/pkg/bitmask/bitmask.go
index 0c2a344..54a5b06 100644
--- a/pkg/bitmask/bitmask.go
+++ b/pkg/bitmask/bitmask.go
@@ -15,8 +15,16 @@
package bitmask
+import (
+ "0xacab.org/leap/bitmask-vpn/pkg/snowflake"
+)
+
+// XXX this interface is a relic of a time in which we had a dual implementation.
+// Nowadays it could be deprecated.
+
type Bitmask interface {
GetStatusCh() <-chan string
+ GetSnowflakeCh() <-chan *snowflake.StatusEvent
Close()
Version() (string, error)
StartVPN(provider string) error
diff --git a/pkg/snowflake/bootstrap.go b/pkg/snowflake/bootstrap.go
index 0f370fa..5e90b0e 100644
--- a/pkg/snowflake/bootstrap.go
+++ b/pkg/snowflake/bootstrap.go
@@ -9,21 +9,57 @@ import (
"log"
"net/http"
"os"
+ "path/filepath"
+ "strconv"
+ "strings"
"time"
"0xacab.org/leap/bitmask-vpn/pkg/config"
"github.com/cretz/bine/tor"
)
+// TODO
+// [ ] fix snowflake-client binary
+// [ ] find tor path
+
const torrc = `UseBridges 1
DataDirectory datadir
-ClientTransportPlugin snowflake exec /usr/local/bin/snowflake-client \
--url https://snowflake-broker.torproject.net.global.prod.fastly.net/ -front cdn.sstatic.net \
--ice stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 \
--max 3
+ClientTransportPlugin snowflake exec /usr/local/bin/snowflake-client -log /tmp/snowflake.log -url https://snowflake-broker.torproject.net.global.prod.fastly.net/ \
+-front cdn.sstatic.net -ice stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 \
+-max 5
+
+Bridge snowflake 192.0.2.3:1
+
+SocksPort auto`
+
+type StatusEvent struct {
+ Progress int
+ Tag string
+}
+
+type StatusLogger struct {
+ ch chan *StatusEvent
+}
-Bridge snowflake 0.0.3.0:1`
+func (e *StatusLogger) Write(p []byte) (n int, err error) {
+ raw := strings.Split(string(p), ":")
+ if len(raw) > 1 {
+ l := raw[1]
+ parts := strings.Split(string(l), " ")
+ if len(parts) > 2 && parts[2] == "STATUS_CLIENT" {
+ if parts[4] == "BOOTSTRAP" {
+ if len(parts) > 6 {
+ pr, _ := strconv.Atoi(parts[5][9:])
+ event := &StatusEvent{Progress: pr, Tag: parts[6][4:]}
+ go func() { e.ch <- event }()
+ }
+ fmt.Println()
+ }
+ }
+ }
+ return len(p), nil
+}
func writeTorrc() string {
f, err := ioutil.TempFile("", "torrc-snowflake-")
@@ -34,9 +70,14 @@ func writeTorrc() string {
return f.Name()
}
-func BootstrapWithSnowflakeProxies() error {
+// TODO pass provider api
+func BootstrapWithSnowflakeProxies(provider string, api string, ch chan *StatusEvent) error {
rcfile := writeTorrc()
- conf := &tor.StartConf{DebugWriter: os.Stdout, TorrcFile: rcfile}
+ logger := &StatusLogger{ch}
+ conf := &tor.StartConf{
+ DebugWriter: logger,
+ TorrcFile: rcfile,
+ }
fmt.Println("Starting Tor and fetching files to bootstrap VPN tunnel...")
fmt.Println("")
@@ -78,14 +119,18 @@ func BootstrapWithSnowflakeProxies() error {
Timeout: time.Minute * 5,
}
- // XXX parametrize these urls
- fetchFile(apiClient, "https://api.black.riseup.net/3/config/eip-service.json")
- fetchFile(apiClient, "https://api.black.riseup.net/3/cert")
+ eipUri := "https://" + api + "/3/config/eip-service.json"
+ eipFile := filepath.Join(config.Path, provider+"-eip.json")
+ fetchFile(apiClient, eipUri, eipFile)
+
+ certUri := "https://" + api + "/3/cert"
+ certFile := filepath.Join(config.Path, provider+".pem")
+ fetchFile(apiClient, certUri, certFile)
return nil
}
-func fetchFile(client *http.Client, uri string) error {
+func fetchFile(client *http.Client, uri string, file string) error {
resp, err := client.Get(uri)
if err != nil {
return err
@@ -96,6 +141,8 @@ func fetchFile(client *http.Client, uri string) error {
if err != nil {
log.Println(err)
}
- fmt.Println(string(c))
- return nil
+ if os.Getenv("DEBUG") == "1" {
+ fmt.Println(string(c))
+ }
+ return ioutil.WriteFile(file, c, 0600)
}
diff --git a/pkg/vpn/bonafide/bonafide.go b/pkg/vpn/bonafide/bonafide.go
index 024a7e1..129845f 100644
--- a/pkg/vpn/bonafide/bonafide.go
+++ b/pkg/vpn/bonafide/bonafide.go
@@ -54,6 +54,8 @@ type Bonafide struct {
maxGateways int
auth authentication
token []byte
+ SnowflakeCh chan *snowflake.StatusEvent
+ snowflake bool
}
type openvpnConfig map[string]interface{}
@@ -206,7 +208,6 @@ func (b *Bonafide) GetPemCertificateNoDNS() ([]byte, error) {
return nil, err
}
defer resp.Body.Close()
-
return ioutil.ReadAll(resp.Body)
}
@@ -241,8 +242,18 @@ func (b *Bonafide) getURLNoDNS(object string) string {
}
func (b *Bonafide) maybeInitializeEIP() error {
+ // FIXME - use config/bitmask flag
if os.Getenv("SNOWFLAKE") == "1" {
- snowflake.BootstrapWithSnowflakeProxies()
+ p := strings.ToLower(config.Provider)
+ // FIXME only if progress != 100 %, then just pick files.
+ // we probably need another status watcher internally, to keep track
+ // of whether we need to cancel, or just wait.
+ snowflake.BootstrapWithSnowflakeProxies(p, getAPIAddr(p), b.SnowflakeCh)
+ err := b.parseEipJSONFromFile()
+ if err != nil {
+ return err
+ }
+ b.gateways = newGatewayPool(b.eip)
} else {
if b.eip == nil {
err := b.fetchEipJSON()
@@ -272,11 +283,11 @@ func (b *Bonafide) GetGateways(transport string) ([]Gateway, error) {
if err != nil {
return nil, err
}
+
max := maxGateways
if b.maxGateways != 0 {
max = b.maxGateways
}
-
gws, err := b.gateways.getBest(transport, b.tzOffsetHours, max)
return gws, err
}
@@ -285,6 +296,7 @@ func (b *Bonafide) GetGateways(transport string) ([]Gateway, error) {
// if "any" is provided it will return all gateways for all transports
func (b *Bonafide) GetAllGateways(transport string) ([]Gateway, error) {
err := b.maybeInitializeEIP()
+ // XXX needs to wait for bonafide too
if err != nil {
return nil, err
}
@@ -327,8 +339,10 @@ func (b *Bonafide) GetGatewayByIP(ip string) (Gateway, error) {
}
func (b *Bonafide) fetchGatewaysFromMenshen() error {
- /* FIXME in float deployments, geolocation is served on gemyip.domain/json, with a LE certificate, but in riseup is served behind the api certificate.
- So this is a workaround until we streamline that behavior */
+ /* FIXME in float deployments, geolocation is served on
+ * gemyip.domain/json, with a LE certificate, but in riseup is served
+ * behind the api certificate. So this is a workaround until we
+ * streamline that behavior */
resp, err := b.client.Post(config.GeolocationAPI, "", nil)
if err != nil {
client := &http.Client{}
diff --git a/pkg/vpn/bonafide/eip_service.go b/pkg/vpn/bonafide/eip_service.go
index 1b8dc01..5b4c3df 100644
--- a/pkg/vpn/bonafide/eip_service.go
+++ b/pkg/vpn/bonafide/eip_service.go
@@ -6,6 +6,7 @@ import (
"io"
"log"
"os"
+ "path/filepath"
"strings"
"time"
@@ -148,6 +149,17 @@ func (b *Bonafide) fetchEipJSON() error {
return nil
}
+func (b *Bonafide) parseEipJSONFromFile() error {
+ provider := strings.ToLower(config.Provider)
+ eipFile := filepath.Join(config.Path, provider+"-eip.json")
+ f, err := os.Open(eipFile)
+ if err != nil {
+ return err
+ }
+ b.eip, err = decodeEIP3(f)
+ return err
+}
+
func decodeEIP3(body io.Reader) (*eipService, error) {
var eip eipService
decoder := json.NewDecoder(body)
diff --git a/pkg/vpn/bonafide/gateways.go b/pkg/vpn/bonafide/gateways.go
index c442e72..25ab027 100644
--- a/pkg/vpn/bonafide/gateways.go
+++ b/pkg/vpn/bonafide/gateways.go
@@ -306,16 +306,17 @@ func (p *gatewayPool) getBestLocation(transport string, tz int) string {
}
func (p *gatewayPool) getAll(transport string, tz int) ([]Gateway, error) {
- /*
- if (&gatewayPool{} == p) {
- log.Println("getAll tried to access uninitialized struct")
- return []Gateway{}, nil
- }
- */
+ if (&gatewayPool{} == p) {
+ log.Println("getAll tried to access uninitialized struct")
+ return []Gateway{}, nil
+ }
+ log.Println(">>> in getAll")
+ log.Println("seems to be initialized...")
if p.recommended == nil || len(p.recommended) == 0 {
return p.getGatewaysFromMenshen(transport, 999)
}
+ log.Println(">>> by timezone")
return p.getGatewaysByTimezone(transport, tz, 999)
}
diff --git a/pkg/vpn/main.go b/pkg/vpn/main.go
index d780afe..cd21b1b 100644
--- a/pkg/vpn/main.go
+++ b/pkg/vpn/main.go
@@ -25,6 +25,7 @@ import (
"0xacab.org/leap/bitmask-vpn/pkg/config"
"0xacab.org/leap/bitmask-vpn/pkg/config/version"
"0xacab.org/leap/bitmask-vpn/pkg/motd"
+ "0xacab.org/leap/bitmask-vpn/pkg/snowflake"
"0xacab.org/leap/bitmask-vpn/pkg/vpn/bonafide"
"0xacab.org/leap/shapeshifter"
"github.com/apparentlymart/go-openvpn-mgmt/openvpn"
@@ -59,7 +60,9 @@ func Init() (*Bitmask, error) {
if err != nil {
return nil, err
}
+ snowCh := make(chan *snowflake.StatusEvent, 20)
bf := bonafide.New()
+ bf.SnowflakeCh = snowCh
launch, err := newLauncher()
if err != nil {
return nil, err
@@ -121,6 +124,10 @@ func (b *Bitmask) GetStatusCh() <-chan string {
return b.statusCh
}
+func (b *Bitmask) GetSnowflakeCh() <-chan *snowflake.StatusEvent {
+ return b.bonafide.SnowflakeCh
+}
+
// Close the connection to bitmask, and does cleanup of temporal files
func (b *Bitmask) Close() {
log.Printf("Close: cleanup and vpn shutdown...")
diff --git a/pkg/vpn/openvpn.go b/pkg/vpn/openvpn.go
index d0fadc1..567b912 100644
--- a/pkg/vpn/openvpn.go
+++ b/pkg/vpn/openvpn.go
@@ -201,6 +201,7 @@ func (b *Bitmask) getCert() (certPath string, err error) {
failed := false
persistentCertFile := filepath.Join(config.Path, strings.ToLower(config.Provider)+".pem")
if _, err := os.Stat(persistentCertFile); !os.IsNotExist(err) && isValidCert(persistentCertFile) {
+ // TODO snowflake might have written a cert here
// reuse cert. for the moment we're not writing one there, this is
// only to allow users to get certs off-band and place them there
// as a last-resort fallback for circumvention.