From fca2ee8d7aeee99e3e24a8cab90c17d7b7c34494 Mon Sep 17 00:00:00 2001 From: "kali kaneko (leap communications)" Date: Tue, 25 Jan 2022 19:54:21 +0100 Subject: [feat] expose snowflake to ui --- branding/templates/snap/hooks/install | 2 +- branding/templates/snap/hooks/remove | 2 +- gui/components/StatusBox.qml | 29 ++++++++++- gui/components/VPNState.qml | 64 +++++++++++++++++++++++++ gui/gui.qrc | 1 + gui/resources/snowflake.svg | 90 +++++++++++++++++++++++++++++++++++ gui/themes/themes.js | 2 + pkg/snowflake/bootstrap.go | 13 ++++- pkg/vpn/bonafide/bonafide.go | 45 +++++++++++++----- 9 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 gui/resources/snowflake.svg diff --git a/branding/templates/snap/hooks/install b/branding/templates/snap/hooks/install index ea85117..166b15a 100755 --- a/branding/templates/snap/hooks/install +++ b/branding/templates/snap/hooks/install @@ -10,7 +10,7 @@ from base64 import decodebytes as decode POLKIT = b'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBvbGljeWNv\nbmZpZyBQVUJMSUMKICItLy9mcmVlZGVza3RvcC8vRFREIFBvbGljeUtpdCBQb2xpY3kgQ29uZmln\ndXJhdGlvbiAxLjAvL0VOIgogImh0dHA6Ly93d3cuZnJlZWRlc2t0b3Aub3JnL3N0YW5kYXJkcy9Q\nb2xpY3lLaXQvMS9wb2xpY3ljb25maWcuZHRkIj4KPHBvbGljeWNvbmZpZz4KCiAgPHZlbmRvcj5M\nRUFQIFByb2plY3Q8L3ZlbmRvcj4KICA8dmVuZG9yX3VybD5odHRwOi8vbGVhcC5zZS88L3ZlbmRv\ncl91cmw+CgogIDxhY3Rpb24gaWQ9InNlLmxlYXAuYml0bWFzay5yaXNldXB2cG4ucG9saWN5Ij4K\nICAgIDxkZXNjcmlwdGlvbj5SdW5zIGJpdG1hc2sgaGVscGVyIHRvIGxhdW5jaCBmaXJld2FsbCBh\nbmQgb3BlbnZwbiAoUmlzZXVwVlBOKTwvZGVzY3JpcHRpb24+CiAgICA8ZGVzY3JpcHRpb24geG1s\nOmxhbmc9ImVzIj5FamVjdXRhIGVsIGFzaXN0ZW50ZSBkZSBiaXRtYXNrIHBhcmEgbGFuemFyIGVs\nIGZpcmV3YWxsIHkgb3BlbnZwbiAoUmlzZXVwVlBOKTwvZGVzY3JpcHRpb24+CiAgICA8bWVzc2Fn\nZT5SaXNldXBWUE4gbmVlZHMgdGhhdCB5b3UgYXV0aGVudGljYXRlIHRvIHN0YXJ0PC9tZXNzYWdl\nPgogICAgPG1lc3NhZ2UgeG1sOmxhbmc9ImVzIj5SaXNldXBWUE4gbmVjZXNpdGEgYXV0b3JpemFj\naW9uIHBhcmEgY29tZW56YXI8L21lc3NhZ2U+CiAgICA8aWNvbl9uYW1lPnBhY2thZ2UteC1nZW5l\ncmljPC9pY29uX25hbWU+IAogICAgPGRlZmF1bHRzPgogICAgICA8YWxsb3dfYW55PnllczwvYWxs\nb3dfYW55PgogICAgICA8YWxsb3dfaW5hY3RpdmU+eWVzPC9hbGxvd19pbmFjdGl2ZT4KICAgICAg\nPGFsbG93X2FjdGl2ZT55ZXM8L2FsbG93X2FjdGl2ZT4KICAgIDwvZGVmYXVsdHM+CiAgICA8YW5u\nb3RhdGUga2V5PSJvcmcuZnJlZWRlc2t0b3AucG9saWN5a2l0LmV4ZWMucGF0aCI+L3NuYXAvYmlu\nL3Jpc2V1cC12cG4uYml0bWFzay1yb290PC9hbm5vdGF0ZT4KICA8L2FjdGlvbj4KPC9wb2xpY3lj\nb25maWc+Cg==\n' -with open('/usr/share/polkit-1/actions/se.leap.bitmask.riseupvpn.policy', 'w') as polkit: +with open('/usr/share/polkit-1/actions/se.leap.bitmask.riseup-vpn.policy', 'w') as polkit: lines = decode(POLKIT).split(b"\n") for line in lines: polkit.write(line.decode() + "\n") diff --git a/branding/templates/snap/hooks/remove b/branding/templates/snap/hooks/remove index fd27b85..70d4c80 100755 --- a/branding/templates/snap/hooks/remove +++ b/branding/templates/snap/hooks/remove @@ -1,6 +1,6 @@ #!/bin/sh echo "Executing remove hook for RiseupVPN" -rm "/usr/share/polkit-1/actions/se.leap.bitmask.riseupvpn.policy" +rm "/usr/share/polkit-1/actions/se.leap.bitmask.riseup-vpn.policy" unlink "/usr/share/applications/riseup-vpn.desktop" || echo "did not remove workaround for global desktop entry" echo "done" diff --git a/gui/components/StatusBox.qml b/gui/components/StatusBox.qml index 7715123..5929033 100644 --- a/gui/components/StatusBox.qml +++ b/gui/components/StatusBox.qml @@ -80,9 +80,32 @@ Item { anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter - text: "" FadeBehavior on text { } } + Label { + id: snowflakeTip + anchors.top: connectionState.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 20 + horizontalAlignment: Text.AlignHCenter + text: qsTr("This can take several minutes") + font.pixelSize: Theme.fontSize * 0.8 + visible: isSnowflakeOn() + } + ProgressBar { + id: snowflakeProgressBar + anchors.top: snowflakeTip.bottom + anchors.horizontalCenter: parent.horizontalCenter + visible: isSnowflakeOn() + value: 0 + } + Label { + id: snowflakeTag + anchors.top: snowflakeProgressBar.bottom + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + visible: isSnowflakeOn() + } } Column { @@ -156,4 +179,8 @@ Item { } } } + + function isSnowflakeOn() { + return ctx != undefined && ctx.snowflakeProgress != "" && ctx.snowflakeProgress != "100" + } } diff --git a/gui/components/VPNState.qml b/gui/components/VPNState.qml index eeeb51d..3a8d49a 100644 --- a/gui/components/VPNState.qml +++ b/gui/components/VPNState.qml @@ -19,11 +19,38 @@ StateGroup { State { name: initializing }, + State { + when: ctx != undefined && ctx.snowflakeProgress != "" && ctx.snowflakeProgress != "100" + // TODO animate image + PropertyChanges { + target: connectionState + text: qsTr("Setting up") + font.pixelSize: Theme.fontSize * 1 + } + PropertyChanges { + target: snowflakeProgressBar + value: parseInt(ctx.snowflakeProgress)/100 + } + PropertyChanges { + target: snowflakeTag + text: getSnowflakeTag() + } + PropertyChanges { + target: statusBoxBackground + border.color: Theme.accentConnecting + } + PropertyChanges { + target: connectionImage + source: Theme.iconSnowflake + anchors.horizontalCenter: parent.horizontalCenter + } + }, State { when: ctx != undefined && ctx.status == "off" && startingUI == true PropertyChanges { target: connectionState text: qsTr("Connecting") + font.pixelSize: Theme.fontSize * 1.5 } PropertyChanges { target: statusBoxBackground @@ -56,6 +83,8 @@ StateGroup { PropertyChanges { target: connectionState text: qsTr("Unsecured\nConnection") + font.pixelSize: Theme.fontSize * 1.5 + visible: true } PropertyChanges { target: statusBoxBackground @@ -89,6 +118,8 @@ StateGroup { PropertyChanges { target: connectionState text: qsTr("Secured\nConnection") + font.pixelSize: Theme.fontSize * 1.5 + visible: true } PropertyChanges { target: statusBoxBackground @@ -123,6 +154,8 @@ StateGroup { PropertyChanges { target: connectionState text: qsTr("Connecting") + font.pixelSize: Theme.fontSize * 1.5 + visible: true } PropertyChanges { target: statusBoxBackground @@ -236,4 +269,35 @@ StateGroup { ctx.appName) // TODO failed is not handled yet } } + + function getSnowflakeTag() { + switch (ctx.snowflakeTag) { + case 'conn_pt': + return qsTr("pluggable transport connection") + case 'conn_done': + return qsTr("connection done") + case 'handshake': + return qsTr("doing handshake") + case 'handshake_done': + return qsTr("handshake done") + case 'onehop_create': + return qsTr("creating one-hop connection") + case 'requesting_status': + return qsTr("requesting status") + case 'loading_status': + return qsTr("loading status") + case 'loading_keys': + return qsTr("loading keys") + case 'requesting_descriptors': + return qsTr("requesting descriptors") + case 'loading_descriptors': + return qsTr("loading descriptors") + case 'circuit_create': + return qsTr("creating circuit") + case 'done': + return qsTr("done") + default: + return ctx.snowflakeTag + } + } } diff --git a/gui/gui.qrc b/gui/gui.qrc index 9e357bf..75ff8cf 100644 --- a/gui/gui.qrc +++ b/gui/gui.qrc @@ -67,6 +67,7 @@ resources/quit.svg resources/alert.svg resources/angle-right.svg + resources/snowflake.svg resources/ravens.svg diff --git a/gui/resources/snowflake.svg b/gui/resources/snowflake.svg new file mode 100644 index 0000000..bdc15ad --- /dev/null +++ b/gui/resources/snowflake.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/themes/themes.js b/gui/themes/themes.js index 6c7ab4e..4c4d3a9 100644 --- a/gui/themes/themes.js +++ b/gui/themes/themes.js @@ -24,6 +24,8 @@ const maxTextWidth = 296; const windowMargin = 16; const popupMargin = 24; +const iconSnowflake = "../resources/snowflake.svg"; + // XXX unused? move definitions here const desktopAppHeight = 520; const desktopAppWidth = 360; diff --git a/pkg/snowflake/bootstrap.go b/pkg/snowflake/bootstrap.go index 5e90b0e..7a093af 100644 --- a/pkg/snowflake/bootstrap.go +++ b/pkg/snowflake/bootstrap.go @@ -22,7 +22,7 @@ import ( // [ ] fix snowflake-client binary // [ ] find tor path -const torrc = `UseBridges 1 +const torrcOrig = `UseBridges 1 DataDirectory datadir ClientTransportPlugin snowflake exec /usr/local/bin/snowflake-client -log /tmp/snowflake.log -url https://snowflake-broker.torproject.net.global.prod.fastly.net/ \ @@ -33,6 +33,17 @@ Bridge snowflake 192.0.2.3:1 SocksPort auto` +const torrc = `UseBridges 1 +DataDirectory datadir + +ClientTransportPlugin snowflake exec /usr/local/bin/snowflake-client -log /tmp/snowflake.log -url https://snowflake-broker.azureedge.net/ \ +-front ajax.aspnetcdn.com -ice stun:stun.l.google.com:19302 \ +-max 5 + +Bridge snowflake 192.0.2.3:1 + +SocksPort auto` + type StatusEvent struct { Progress int Tag string diff --git a/pkg/vpn/bonafide/bonafide.go b/pkg/vpn/bonafide/bonafide.go index 129845f..0fa48ed 100644 --- a/pkg/vpn/bonafide/bonafide.go +++ b/pkg/vpn/bonafide/bonafide.go @@ -47,15 +47,16 @@ const ( ) type Bonafide struct { - client httpClient - eip *eipService - tzOffsetHours int - gateways *gatewayPool - maxGateways int - auth authentication - token []byte - SnowflakeCh chan *snowflake.StatusEvent - snowflake bool + client httpClient + eip *eipService + tzOffsetHours int + gateways *gatewayPool + maxGateways int + auth authentication + token []byte + SnowflakeCh chan *snowflake.StatusEvent + snowflakeProgress int + snowflake bool } type openvpnConfig map[string]interface{} @@ -241,14 +242,32 @@ func (b *Bonafide) getURLNoDNS(object string) string { return "" } +func (b *Bonafide) watchSnowflakeProgress(ch chan *snowflake.StatusEvent) { + // We need to keep track of the bootstrap process here, and then we + // pass to the channel that is observed by the backend + log.Println(">>> WATCH SNOWFLAKE") + go func() { + for { + select { + case evt := <-ch: + b.snowflakeProgress = evt.Progress + b.SnowflakeCh <- evt + } + } + + }() +} + func (b *Bonafide) maybeInitializeEIP() error { // FIXME - use config/bitmask flag if os.Getenv("SNOWFLAKE") == "1" { 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) + log.Println(b.snowflakeProgress) + if b.snowflakeProgress != 100 { + ch := make(chan *snowflake.StatusEvent, 20) + b.watchSnowflakeProgress(ch) + snowflake.BootstrapWithSnowflakeProxies(p, getAPIAddr(p), ch) + } err := b.parseEipJSONFromFile() if err != nil { return err -- cgit v1.2.3