summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-11-24 16:18:51 +0100
committerkali kaneko (leap communications) <kali@leap.se>2021-11-24 20:00:11 +0100
commit4fca210846b28caf1372edb5ec0abe8193d3ff8b (patch)
treebde7c869b580d1527c7c09bd85d25dabf01a92e7
parent7d10b4ce74fe9e8a78689d95824fc3bfc37eb469 (diff)
[feat] hook motd during bootstrap
some refactor, plus fix docs
-rw-r--r--branding/motd-cli/README.md6
-rw-r--r--branding/motd-cli/main.go123
-rw-r--r--branding/motd-cli/motd-example.json15
-rw-r--r--pkg/backend/init.go6
-rw-r--r--pkg/backend/status.go4
-rw-r--r--pkg/bitmask/bitmask.go3
-rw-r--r--pkg/bitmask/init.go1
-rw-r--r--pkg/config/version/checknewer.go6
-rw-r--r--pkg/motd/fetch.go54
-rw-r--r--pkg/motd/motd-example.json15
-rw-r--r--pkg/motd/motd.go118
-rw-r--r--pkg/motd/motd_test.go (renamed from branding/motd-cli/motd_test.go)10
-rw-r--r--pkg/vpn/main.go50
13 files changed, 267 insertions, 144 deletions
diff --git a/branding/motd-cli/README.md b/branding/motd-cli/README.md
index 5d00f10..5e66cee 100644
--- a/branding/motd-cli/README.md
+++ b/branding/motd-cli/README.md
@@ -15,8 +15,8 @@ The structure of the `motd.json` file is like follows:
```
{
"motd": [{
- "begin": "Jan 1 2021 00:00:00",
- "end": "Dec 31 2021 23:59:00",
+ "begin": "01 Nov 21 00:00 -0700",
+ "end": "31 Jan 22 00:00 -0700",
"type": "daily",
"platform": "all",
"urgency": "normal",
@@ -32,7 +32,7 @@ The structure of the `motd.json` file is like follows:
Valid values are:
-* Begin, End are date strings, like "Jan 1 2021 00:00:00".
+* Begin, End are date strings, in the format: "01 Jan 21 00:00:00 -0700".
* Type: "once" for a one-shot message, "daily" for a message that is displayed daily during the specified duration.
* Platform: one of "windows", "osx", "snap", "linux", or "all".
* Urgency: either "normal" or "critical".
diff --git a/branding/motd-cli/main.go b/branding/motd-cli/main.go
index 0ac1316..cea5910 100644
--- a/branding/motd-cli/main.go
+++ b/branding/motd-cli/main.go
@@ -1,110 +1,19 @@
package main
import (
- "encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
- "log"
"net/http"
"os"
- "time"
-)
+ "path/filepath"
-/* TODO move structs to pkg/config/motd module, import from there */
+ "0xacab.org/leap/bitmask-vpn/pkg/motd"
+)
-const defaultFile = "motd-example.json"
const OK = "✓"
const WRONG = "☓"
-const TimeString = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
-
-type Messages struct {
- Messages []Message `json:"motd"`
-}
-
-func (m *Messages) Length() int {
- return len(m.Messages)
-}
-
-type Message struct {
- Begin string `json:"begin"`
- End string `json:"end"`
- Type string `json:"type"`
- Platform string `json:"platform"`
- Urgency string `json:"urgency"`
- Text []LocalizedText `json:"text"`
-}
-
-func (m *Message) IsValid() bool {
- valid := (m.IsValidBegin() && m.IsValidEnd() &&
- m.IsValidType() && m.IsValidPlatform() && m.IsValidUrgency() &&
- m.HasLocalizedText())
- return valid
-}
-
-func (m *Message) IsValidBegin() bool {
- _, err := time.Parse(TimeString, m.Begin)
- if err != nil {
- log.Println(err)
- return false
- }
- return true
-}
-
-func (m *Message) IsValidEnd() bool {
- endTime, err := time.Parse(TimeString, m.End)
- if err != nil {
- log.Println(err)
- return false
- }
- beginTime, err := time.Parse(TimeString, m.Begin)
- if err != nil {
- log.Println(err)
- return false
- }
- if !beginTime.Before(endTime) {
- log.Println("begin ts should be before end")
- return false
- }
- return true
-}
-
-func (m *Message) IsValidType() bool {
- switch m.Type {
- case "once", "daily":
- return true
- default:
- return false
- }
-}
-
-func (m *Message) IsValidPlatform() bool {
- switch m.Platform {
- case "windows", "linux", "osx", "all":
- return true
- default:
- return false
- }
-}
-
-func (m *Message) IsValidUrgency() bool {
- switch m.Urgency {
- case "normal", "critical":
- return true
- default:
- return false
- }
-}
-
-func (m *Message) HasLocalizedText() bool {
- return len(m.Text) > 0
-}
-
-type LocalizedText struct {
- Lang string `json:"lang"`
- Str string `json:"str"`
-}
func main() {
file := flag.String("file", "", "file to validate")
@@ -114,16 +23,19 @@ func main() {
f := *file
u := *url
+ var m motd.Messages
+ var err error
+
if u != "" {
fmt.Println("url:", u)
f = downloadToTempFile(u)
} else {
if f == "" {
- f = defaultFile
+ f = filepath.Join("../../pkg/motd/", motd.ExampleFile)
}
fmt.Println("file:", f)
}
- m, err := parseFile(f)
+ m, err = motd.ParseFile(f)
if err != nil {
panic(err)
}
@@ -159,25 +71,6 @@ func downloadToTempFile(url string) string {
return out.Name()
}
-func parseFile(f string) (Messages, error) {
- jsonFile, err := os.Open(f)
- if err != nil {
- panic(err)
- }
- defer jsonFile.Close()
- byteVal, err := ioutil.ReadAll(jsonFile)
- if err != nil {
- panic(err)
- }
- return parseJsonStr(byteVal)
-}
-
-func parseJsonStr(b []byte) (Messages, error) {
- var m Messages
- json.Unmarshal(b, &m)
- return m, nil
-}
-
func mark(val bool) string {
if val {
return OK
diff --git a/branding/motd-cli/motd-example.json b/branding/motd-cli/motd-example.json
deleted file mode 100644
index f33c2b9..0000000
--- a/branding/motd-cli/motd-example.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "motd": [{
- "begin": "02 Jan 21 15:04 -0700",
- "end": "31 Jan 22 15:04 -0700",
- "type": "daily",
- "platform": "all",
- "urgency": "normal",
- "text": [
- { "lang": "en",
- "str": "This is a <a href='https://leap.se'>test!</a>"},
- { "lang": "es",
- "str": "Esto es una <a href='https://leap.se'>pruebita!</a>"}
- ]}
- ]
-}
diff --git a/pkg/backend/init.go b/pkg/backend/init.go
index 70a3582..c6d713b 100644
--- a/pkg/backend/init.go
+++ b/pkg/backend/init.go
@@ -17,8 +17,10 @@ func initializeContext(opts *InitOpts) {
var st status = off
// TODO - now there's really no need to dance between opts and config anymore
- // but this was the simplest transition. We should probably keep the multi-provider config in the backend too, and just
- // switch the "active" here in the ctx, after the user has selected one in the combobox.
+ // but this was the simplest transition. We should probably keep the
+ // multi-provider config in the backend too, and just
+ // switch the "active" here in the ctx, after the user has selected one
+ // in the combobox.
ctx = &connectionCtx{
AppName: opts.ProviderOptions.AppName,
Provider: opts.ProviderOptions.Provider,
diff --git a/pkg/backend/status.go b/pkg/backend/status.go
index 79b70ff..f21fddd 100644
--- a/pkg/backend/status.go
+++ b/pkg/backend/status.go
@@ -54,6 +54,8 @@ type connectionCtx struct {
UseUDP bool `json:"udp"`
ManualLocation bool `json:"manualLocation"`
IsReady bool `json:"isReady"`
+ CanUpgrade bool `json:"canUpgrade"`
+ Motd string `json:"motd"`
bm bitmask.Bitmask
autostart bitmask.Autostart
cfg *config.Config
@@ -72,6 +74,8 @@ func (c *connectionCtx) toJson() ([]byte, error) {
c.Transport = transport
c.UseUDP = c.cfg.UDP // TODO initialize bitmask too
c.ManualLocation = c.bm.IsManualLocation()
+ c.CanUpgrade = c.bm.CanUpgrade()
+ c.Motd = c.bm.GetMotd()
}
defer statusMutex.Unlock()
b, err := json.Marshal(c)
diff --git a/pkg/bitmask/bitmask.go b/pkg/bitmask/bitmask.go
index 5597efb..d02487b 100644
--- a/pkg/bitmask/bitmask.go
+++ b/pkg/bitmask/bitmask.go
@@ -32,6 +32,7 @@ type Bitmask interface {
GetBestLocation(protocol string) string
UseGateway(name string)
UseAutomaticGateway()
+ SetProvider(string)
GetTransport() string
SetTransport(string) error
UseUDP(bool) error
@@ -41,4 +42,6 @@ type Bitmask interface {
IsManualLocation() bool
NeedsCredentials() bool
DoLogin(username, password string) (bool, error)
+ CanUpgrade() bool
+ GetMotd() string
}
diff --git a/pkg/bitmask/init.go b/pkg/bitmask/init.go
index ab40fed..bc7d47d 100644
--- a/pkg/bitmask/init.go
+++ b/pkg/bitmask/init.go
@@ -88,6 +88,7 @@ func InitializeBitmask(conf *config.Config) (Bitmask, error) {
if err != nil {
return nil, err
}
+ b.SetProvider(config.Provider)
err = setTransport(b, conf)
if err != nil {
diff --git a/pkg/config/version/checknewer.go b/pkg/config/version/checknewer.go
index 4b89c34..115696a 100644
--- a/pkg/config/version/checknewer.go
+++ b/pkg/config/version/checknewer.go
@@ -48,9 +48,9 @@ func CanUpgrade() bool {
if os.Getenv("DEBUG") == "1" {
log.Println(">>> Remote version: " + r)
log.Println(">>> Current version: " + VERSION)
- if canUpgrade {
- log.Println("Newer version available")
- }
+ }
+ if canUpgrade {
+ log.Println("There's a newer version available:", r)
}
return canUpgrade
}
diff --git a/pkg/motd/fetch.go b/pkg/motd/fetch.go
new file mode 100644
index 0000000..4f22388
--- /dev/null
+++ b/pkg/motd/fetch.go
@@ -0,0 +1,54 @@
+package motd
+
+import (
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+
+ "0xacab.org/leap/bitmask-vpn/pkg/config"
+)
+
+func FetchLatest() []Message {
+ empty := []Message{}
+ if os.Getenv("SKIP_MOTD") == "1" {
+ return empty
+ }
+ url := ""
+ switch config.Provider {
+ case "riseup.net":
+ url = "https://downloads.leap.se/motd/riseup/motd.json"
+ default:
+ return empty
+ }
+ log.Println("Fetching MOTD for", config.Provider)
+ b, err := fetchURL(url)
+ if err != nil {
+ log.Println("WARN Error fetching json from", url)
+ return empty
+ }
+ allMsg, err := getFromJSON(b)
+ if err != nil {
+ log.Println("WARN Error parsing json from", url)
+ return empty
+ }
+ valid := empty[:]
+ if allMsg.Length() != 0 {
+ log.Printf("There are %d pending messages\n", allMsg.Length())
+ }
+ for _, msg := range allMsg.Messages {
+ if msg.IsValid() {
+ valid = append(valid, msg)
+ }
+ }
+ return valid
+}
+
+func fetchURL(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+ return ioutil.ReadAll(resp.Body)
+}
diff --git a/pkg/motd/motd-example.json b/pkg/motd/motd-example.json
new file mode 100644
index 0000000..20a7727
--- /dev/null
+++ b/pkg/motd/motd-example.json
@@ -0,0 +1,15 @@
+{
+ "motd": [{
+ "begin": "01 Nov 21 00:00 -0700",
+ "end": "31 Jan 22 00:00 -0700",
+ "type": "daily",
+ "platform": "all",
+ "urgency": "normal",
+ "text": [
+ { "lang": "en",
+ "str": "Thanks for using RiseupVPN! Please report us <a href='https://0xacab.org/leap/bitmask-vpn'>any issue or feature request</a>."},
+ { "lang": "es",
+ "str": "¡Gracias por usar RiseupVPN! Por favor reportanos <a href='https://0xacab.org/leap/bitmask-vpn'>cualquier bug o petición</a>."}
+ ]}
+ ]
+}
diff --git a/pkg/motd/motd.go b/pkg/motd/motd.go
new file mode 100644
index 0000000..db21025
--- /dev/null
+++ b/pkg/motd/motd.go
@@ -0,0 +1,118 @@
+package motd
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+)
+
+const TimeString = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
+const ExampleFile = "motd-example.json"
+
+func ParseFile(f string) (Messages, error) {
+ jsonFile, err := os.Open(f)
+ if err != nil {
+ panic(err)
+ }
+ defer jsonFile.Close()
+ byteVal, err := ioutil.ReadAll(jsonFile)
+ if err != nil {
+ panic(err)
+ }
+ return getFromJSON(byteVal)
+}
+
+func getFromJSON(b []byte) (Messages, error) {
+ var m Messages
+ json.Unmarshal(b, &m)
+ return m, nil
+}
+
+type Messages struct {
+ Messages []Message `json:"motd"`
+}
+
+func (m *Messages) Length() int {
+ return len(m.Messages)
+}
+
+type Message struct {
+ Begin string `json:"begin"`
+ End string `json:"end"`
+ Type string `json:"type"`
+ Platform string `json:"platform"`
+ Urgency string `json:"urgency"`
+ Text []LocalizedText `json:"text"`
+}
+
+func (m *Message) IsValid() bool {
+ valid := (m.IsValidBegin() && m.IsValidEnd() &&
+ m.IsValidType() && m.IsValidPlatform() && m.IsValidUrgency() &&
+ m.HasLocalizedText())
+ return valid
+}
+
+func (m *Message) IsValidBegin() bool {
+ _, err := time.Parse(TimeString, m.Begin)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ return true
+}
+
+func (m *Message) IsValidEnd() bool {
+ endTime, err := time.Parse(TimeString, m.End)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ beginTime, err := time.Parse(TimeString, m.Begin)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ if !beginTime.Before(endTime) {
+ log.Println("begin ts should be before end")
+ return false
+ }
+ return true
+}
+
+func (m *Message) IsValidType() bool {
+ switch m.Type {
+ case "once", "daily":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) IsValidPlatform() bool {
+ switch m.Platform {
+ case "windows", "linux", "osx", "all":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) IsValidUrgency() bool {
+ switch m.Urgency {
+ case "normal", "critical":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) HasLocalizedText() bool {
+ return len(m.Text) > 0
+}
+
+type LocalizedText struct {
+ Lang string `json:"lang"`
+ Str string `json:"str"`
+}
diff --git a/branding/motd-cli/motd_test.go b/pkg/motd/motd_test.go
index ed11661..85a2f1c 100644
--- a/branding/motd-cli/motd_test.go
+++ b/pkg/motd/motd_test.go
@@ -1,11 +1,11 @@
-package main
+package motd
import (
"testing"
)
func TestGoodMotd(t *testing.T) {
- m, err := parseFile(defaultFile)
+ m, err := ParseFile(ExampleFile)
if err != nil {
t.Errorf("error parsing default file")
}
@@ -14,7 +14,7 @@ func TestGoodMotd(t *testing.T) {
}
for _, msg := range m.Messages {
if !msg.IsValid() {
- t.Errorf("invalid motd json at %s", defaultFile)
+ t.Errorf("invalid motd json at %s", ExampleFile)
}
}
}
@@ -35,7 +35,7 @@ const emptyDate = `
}`
func TestEmptyDateFails(t *testing.T) {
- m, err := parseJsonStr([]byte(emptyDate))
+ m, err := getFromJSON([]byte(emptyDate))
if err != nil {
t.Errorf("error parsing json")
}
@@ -60,7 +60,7 @@ const badEnd = `
}`
func TestBadEnd(t *testing.T) {
- m, err := parseJsonStr([]byte(badEnd))
+ m, err := getFromJSON([]byte(badEnd))
if err != nil {
t.Errorf("error parsing json")
}
diff --git a/pkg/vpn/main.go b/pkg/vpn/main.go
index 0b1d316..619a10f 100644
--- a/pkg/vpn/main.go
+++ b/pkg/vpn/main.go
@@ -16,11 +16,14 @@
package vpn
import (
+ "encoding/json"
"io/ioutil"
"log"
"os"
"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/vpn/bonafide"
"0xacab.org/leap/shapeshifter"
"github.com/apparentlymart/go-openvpn-mgmt/openvpn"
@@ -41,6 +44,9 @@ type Bitmask struct {
openvpnArgs []string
udp bool
failed bool
+ canUpgrade bool
+ motd []motd.Message
+ provider string
}
// Init the connection to bitmask
@@ -55,8 +61,18 @@ func Init() (*Bitmask, error) {
if err != nil {
return nil, err
}
- b := Bitmask{tempdir, bonafide.Gateway{}, bonafide.Gateway{}, statusCh, nil, bf, launch, "", nil, "", []string{}, false, false}
+ b := Bitmask{
+ tempdir,
+ bonafide.Gateway{},
+ bonafide.Gateway{}, statusCh, nil, bf, launch,
+ "", nil, "", []string{},
+ false, false, false,
+ []motd.Message{}, ""}
+ // FIXME multiprovider: need to pass provider name early on
+ // XXX we want to block on these, but they can timeout if we're blocked.
+ b.checkForUpgrades()
+ b.checkForMOTD()
b.launch.firewallStop()
/*
TODO -- we still want to do this, since it resets the fw/vpn if running
@@ -77,6 +93,26 @@ func Init() (*Bitmask, error) {
return &b, err
}
+func (b *Bitmask) SetProvider(p string) {
+ b.provider = p
+}
+
+func (b *Bitmask) checkForUpgrades() {
+
+ // SNAPS have their own way of upgrading. We probably should also try to detect
+ // if we've been installed via another package manager.
+ // For now, it's maybe a good idea to disable the UI check in linux, and be
+ // way more strict in windows/osx.
+ if os.Getenv("SNAP") != "" {
+ return
+ }
+ b.canUpgrade = version.CanUpgrade()
+}
+
+func (b *Bitmask) checkForMOTD() {
+ b.motd = motd.FetchLatest()
+}
+
// GetStatusCh returns a channel that will recieve VPN status changes
func (b *Bitmask) GetStatusCh() <-chan string {
return b.statusCh
@@ -113,3 +149,15 @@ func (b *Bitmask) UseUDP(udp bool) error {
b.udp = udp
return nil
}
+
+func (b *Bitmask) GetMotd() string {
+ bytes, err := json.Marshal(b.motd)
+ if err != nil {
+ log.Println("WARN error marshalling motd")
+ }
+ return string(bytes)
+}
+
+func (b *Bitmask) CanUpgrade() bool {
+ return b.canUpgrade
+}