summaryrefslogtreecommitdiff
path: root/standalone
diff options
context:
space:
mode:
Diffstat (limited to 'standalone')
-rw-r--r--standalone/bonafide.go231
-rw-r--r--standalone/bonafide_test.go42
-rw-r--r--standalone/launcher_linux.go129
-rw-r--r--standalone/launcher_windows.go73
-rw-r--r--standalone/main.go89
-rw-r--r--standalone/status.go91
-rw-r--r--standalone/vpn.go121
7 files changed, 776 insertions, 0 deletions
diff --git a/standalone/bonafide.go b/standalone/bonafide.go
new file mode 100644
index 0000000..449383e
--- /dev/null
+++ b/standalone/bonafide.go
@@ -0,0 +1,231 @@
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ certAPI = "https://api.black.riseup.net/1/cert"
+ eipAPI = "https://api.black.riseup.net/1/config/eip-service.json"
+)
+
+var (
+ caCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIBATANBgkqhkiG9w0BAQ0FADBZMRgwFgYDVQQKDA9SaXNl
+dXAgTmV0d29ya3MxGzAZBgNVBAsMEmh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UE
+AwwXUmlzZXVwIE5ldHdvcmtzIFJvb3QgQ0EwHhcNMTQwNDI4MDAwMDAwWhcNMjQw
+NDI4MDAwMDAwWjBZMRgwFgYDVQQKDA9SaXNldXAgTmV0d29ya3MxGzAZBgNVBAsM
+Emh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UEAwwXUmlzZXVwIE5ldHdvcmtzIFJv
+b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC76J4ciMJ8Sg0m
+TP7DF2DT9zNe0Csk4myoMFC57rfJeqsAlJCv1XMzBmXrw8wq/9z7XHv6n/0sWU7a
+7cF2hLR33ktjwODlx7vorU39/lXLndo492ZBhXQtG1INMShyv+nlmzO6GT7ESfNE
+LliFitEzwIegpMqxCIHXFuobGSCWF4N0qLHkq/SYUMoOJ96O3hmPSl1kFDRMtWXY
+iw1SEKjUvpyDJpVs3NGxeLCaA7bAWhDY5s5Yb2fA1o8ICAqhowurowJpW7n5ZuLK
+5VNTlNy6nZpkjt1QycYvNycffyPOFm/Q/RKDlvnorJIrihPkyniV3YY5cGgP+Qkx
+HUOT0uLA6LHtzfiyaOqkXwc4b0ZcQD5Vbf6Prd20Ppt6ei0zazkUPwxld3hgyw58
+m/4UIjG3PInWTNf293GngK2Bnz8Qx9e/6TueMSAn/3JBLem56E0WtmbLVjvko+LF
+PM5xA+m0BmuSJtrD1MUCXMhqYTtiOvgLBlUm5zkNxALzG+cXB28k6XikXt6MRG7q
+hzIPG38zwkooM55yy5i1YfcIi5NjMH6A+t4IJxxwb67MSb6UFOwg5kFokdONZcwj
+shczHdG9gLKSBIvrKa03Nd3W2dF9hMbRu//STcQxOailDBQCnXXfAATj9pYzdY4k
+ha8VCAREGAKTDAex9oXf1yRuktES4QIDAQABo2AwXjAdBgNVHQ4EFgQUC4tdmLVu
+f9hwfK4AGliaet5KkcgwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMBAf8wHwYD
+VR0jBBgwFoAUC4tdmLVuf9hwfK4AGliaet5KkcgwDQYJKoZIhvcNAQENBQADggIB
+AGzL+GRnYu99zFoy0bXJKOGCF5XUXP/3gIXPRDqQf5g7Cu/jYMID9dB3No4Zmf7v
+qHjiSXiS8jx1j/6/Luk6PpFbT7QYm4QLs1f4BlfZOti2KE8r7KRDPIecUsUXW6P/
+3GJAVYH/+7OjA39za9AieM7+H5BELGccGrM5wfl7JeEz8in+V2ZWDzHQO4hMkiTQ
+4ZckuaL201F68YpiItBNnJ9N5nHr1MRiGyApHmLXY/wvlrOpclh95qn+lG6/2jk7
+3AmihLOKYMlPwPakJg4PYczm3icFLgTpjV5sq2md9bRyAg3oPGfAuWHmKj2Ikqch
+Td5CHKGxEEWbGUWEMP0s1A/JHWiCbDigc4Cfxhy56CWG4q0tYtnc2GMw8OAUO6Wf
+Xu5pYKNkzKSEtT/MrNJt44tTZWbKV/Pi/N2Fx36my7TgTUj7g3xcE9eF4JV2H/sg
+tsK3pwE0FEqGnT4qMFbixQmc8bGyuakr23wjMvfO7eZUxBuWYR2SkcP26sozF9PF
+tGhbZHQVGZUTVPyvwahMUEhbPGVerOW0IYpxkm0x/eaWdTc4vPpf/rIlgbAjarnJ
+UN9SaWRlWKSdP4haujnzCoJbM7dU9bjvlGZNyXEekgeT0W2qFeGGp+yyUWw8tNsp
+0BuC1b7uW/bBn/xKm319wXVDvBgZgcktMolak39V7DVO
+-----END CERTIFICATE-----`)
+)
+
+type bonafide struct {
+ client *http.Client
+ eip *eipService
+ defaultGateway string
+}
+
+type eipService struct {
+ Gateways []gateway
+ Locations map[string]struct {
+ CountryCode string
+ Hemisphere string
+ Name string
+ Timezone string
+ }
+ OpenvpnConfiguration map[string]interface{} `json:"openvpn_configuration"`
+}
+
+type gateway struct {
+ Capabilities struct {
+ Ports []string
+ Protocols []string
+ }
+ Host string
+ IPAddress string `json:"ip_address"`
+ Location string
+}
+
+func newBonafide() *bonafide {
+ certs := x509.NewCertPool()
+ certs.AppendCertsFromPEM(caCert)
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ RootCAs: certs,
+ },
+ },
+ }
+
+ return &bonafide{client, nil, ""}
+}
+
+func (b *bonafide) getCertPem() ([]byte, error) {
+ resp, err := b.client.Post(certAPI, "", nil)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("get vpn cert has failed with status: %s", resp.Status)
+ }
+
+ return ioutil.ReadAll(resp.Body)
+}
+
+func (b *bonafide) getGateways() ([]gateway, error) {
+ if b.eip == nil {
+ err := b.fetchEipJSON()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return b.eip.Gateways, nil
+}
+
+func (b *bonafide) setDefaultGateway(name string) {
+ b.defaultGateway = name
+ b.sortGateways()
+}
+
+func (b *bonafide) getOpenvpnArgs() ([]string, error) {
+ if b.eip == nil {
+ err := b.fetchEipJSON()
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ args := []string{}
+ for arg, value := range b.eip.OpenvpnConfiguration {
+ switch v := value.(type) {
+ case string:
+ args = append(args, "--"+arg)
+ args = append(args, strings.Split(v, " ")...)
+ case bool:
+ if v {
+ args = append(args, "--"+arg)
+ }
+ default:
+ log.Printf("Uknwon openvpn argument type: %s - %v", arg, value)
+ }
+ }
+ return args, nil
+}
+
+func (b *bonafide) fetchEipJSON() error {
+ resp, err := b.client.Post(eipAPI, "", nil)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ return fmt.Errorf("get vpn cert has failed with status: %s", resp.Status)
+ }
+
+ var eip eipService
+ decoder := json.NewDecoder(resp.Body)
+ err = decoder.Decode(&eip)
+ if err != nil {
+ return err
+ }
+
+ b.eip = &eip
+ b.sortGateways()
+ return nil
+}
+
+func (b *bonafide) sortGateways() {
+ type gatewayDistance struct {
+ gateway gateway
+ distance int
+ }
+
+ gws := []gatewayDistance{}
+ _, tzOffset := time.Now().Zone()
+ for _, gw := range b.eip.Gateways {
+ distance := 13
+ if gw.Location == b.defaultGateway {
+ distance = -1
+ } else {
+ gwOffset, err := strconv.Atoi(b.eip.Locations[gw.Location].Timezone)
+ if err != nil {
+ log.Printf("Error sorting gateways: %v", err)
+ } else {
+ distance = tzDistance(tzOffset, gwOffset)
+ }
+ }
+ gws = append(gws, gatewayDistance{gw, distance})
+ }
+
+ sort.Slice(gws, func(i, j int) bool { return gws[i].distance > gws[j].distance })
+ for i, gw := range gws {
+ b.eip.Gateways[i] = gw.gateway
+ }
+}
+
+func tzDistance(offset1, offset2 int) int {
+ abs := func(x int) int {
+ if x < 0 {
+ return -x
+ } else {
+ return x
+ }
+ }
+ distance := abs(offset1 - offset2)
+ if distance > 12 {
+ distance = 24 - distance
+ }
+ return distance
+}
diff --git a/standalone/bonafide_test.go b/standalone/bonafide_test.go
new file mode 100644
index 0000000..152b108
--- /dev/null
+++ b/standalone/bonafide_test.go
@@ -0,0 +1,42 @@
+package bitmask
+
+import (
+ "bytes"
+ "testing"
+)
+
+var (
+ privateKeyHeader = []byte("-----BEGIN RSA PRIVATE KEY-----")
+ certHeader = []byte("-----BEGIN CERTIFICATE-----")
+)
+
+func TestGetCert(t *testing.T) {
+ b := newBonafide()
+ cert, err := b.getCertPem()
+ if err != nil {
+ t.Fatal("getCert returned an error: ", err)
+ }
+
+ if !bytes.Contains(cert, privateKeyHeader) {
+ t.Errorf("No private key present: \n%q", cert)
+ }
+
+ if !bytes.Contains(cert, certHeader) {
+ t.Errorf("No cert present: \n%q", cert)
+ }
+}
+
+func TestGetGateways(t *testing.T) {
+ b := newBonafide()
+ gateways, err := b.getGateways()
+ if err != nil {
+ t.Fatal("getGateways returned an error: ", err)
+ }
+
+ for _, gw := range gateways {
+ if gw.IPAddress == "5.79.86.180" {
+ return
+ }
+ }
+ t.Errorf("5.79.86.180 not in the list")
+}
diff --git a/standalone/launcher_linux.go b/standalone/launcher_linux.go
new file mode 100644
index 0000000..4046fa5
--- /dev/null
+++ b/standalone/launcher_linux.go
@@ -0,0 +1,129 @@
+// +build linux
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "errors"
+ "log"
+ "os"
+ "os/exec"
+)
+
+const (
+ systemOpenvpnPath = "/usr/sbin/openvpn"
+ snapOpenvpnPath = "/snap/bin/riseup-vpn.openvpn"
+)
+
+var bitmaskRootPaths = []string{
+ "/usr/sbin/bitmask-root",
+ "/usr/local/sbin/bitmask-root",
+ "/snap/bin/riseup-vpn.bitmask-root",
+}
+
+type launcher struct {
+ openvpnCh chan []string
+}
+
+func newLauncher() (*launcher, error) {
+ l := launcher{make(chan []string, 1)}
+ go l.openvpnRunner()
+ return &l, nil
+}
+
+func (l *launcher) close() error {
+ return nil
+}
+
+func (l *launcher) openvpnStart(flags ...string) error {
+ log.Println("openvpn start: ", flags)
+ arg := []string{"openvpn", "start", getOpenvpnPath()}
+ arg = append(arg, flags...)
+ l.openvpnCh <- arg
+ return nil
+}
+
+func (l *launcher) openvpnStop() error {
+ l.openvpnCh <- nil
+ log.Println("openvpn stop")
+ return runBitmaskRoot("openvpn", "stop")
+}
+
+func (l *launcher) firewallStart(gateways []gateway) error {
+ log.Println("firewall start")
+ arg := []string{"firewall", "start"}
+ for _, gw := range gateways {
+ arg = append(arg, gw.IPAddress)
+ }
+ return runBitmaskRoot(arg...)
+}
+
+func (l *launcher) firewallStop() error {
+ log.Println("firewall stop")
+ return runBitmaskRoot("firewall", "stop")
+}
+
+func (l *launcher) openvpnRunner(arg ...string) {
+ running := false
+ runOpenvpn := func(arg []string) {
+ for running {
+ err := runBitmaskRoot(arg...)
+ if err != nil {
+ log.Printf("An error ocurred running openvpn: %v", err)
+ }
+ }
+ }
+
+ for arg := range l.openvpnCh {
+ if arg == nil {
+ running = false
+ } else {
+ running = true
+ go runOpenvpn(arg)
+ }
+ }
+}
+
+func runBitmaskRoot(arg ...string) error {
+ bitmaskRoot, err := bitmaskRootPath()
+ if err != nil {
+ return err
+ }
+ arg = append([]string{bitmaskRoot}, arg...)
+
+ cmd := exec.Command("pkexec", arg...)
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func bitmaskRootPath() (string, error) {
+ for _, path := range bitmaskRootPaths {
+ if _, err := os.Stat(path); !os.IsNotExist(err) {
+ return path, nil
+ }
+ }
+ return "", errors.New("No bitmask-root found")
+}
+
+func getOpenvpnPath() string {
+ if os.Getenv("SNAP") != "" {
+ return snapOpenvpnPath
+ }
+ return systemOpenvpnPath
+}
diff --git a/standalone/launcher_windows.go b/standalone/launcher_windows.go
new file mode 100644
index 0000000..c8e79b9
--- /dev/null
+++ b/standalone/launcher_windows.go
@@ -0,0 +1,73 @@
+// +build windows
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "encoding/json"
+ "net/textproto"
+ "strings"
+)
+
+const (
+ helperAddr = "localhost:7171"
+)
+
+type launcher struct {
+ conn *textproto.Conn
+}
+
+func newLauncher() (*launcher, error) {
+ conn, err := textproto.Dial("tcp", helperAddr)
+ return &launcher{conn}, err
+}
+
+func (l *launcher) close() error {
+ return l.conn.Close()
+}
+
+func (l *launcher) openvpnStart(flags ...string) error {
+ return l.send("openvpn_start", flags...)
+}
+
+func (l *launcher) openvpnStop() error {
+ return l.send("openvpn_stop")
+}
+
+func (l *launcher) firewallStart(gateways []gateway) error {
+ return nil
+}
+
+func (l *launcher) firewallStop() error {
+ return nil
+}
+
+func (l *launcher) send(cmd string, args ...string) error {
+ if args == nil {
+ args = []string{"null"}
+ }
+ command := struct {
+ Cmd string `json:"cmd"`
+ Args string `json:"args"`
+ }{cmd, strings.Join(args, " ")}
+ bytesCmd, err := json.Marshal(command)
+ if err != nil {
+ return err
+ }
+
+ _, err = l.conn.Cmd(string(bytesCmd))
+ return err
+}
diff --git a/standalone/main.go b/standalone/main.go
new file mode 100644
index 0000000..357d53f
--- /dev/null
+++ b/standalone/main.go
@@ -0,0 +1,89 @@
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+
+ "github.com/apparentlymart/go-openvpn-mgmt/openvpn"
+)
+
+// Bitmask holds the bitmask client data
+type Bitmask struct {
+ tempdir string
+ statusCh chan string
+ managementClient *openvpn.MgmtClient
+ bonafide *bonafide
+ launch *launcher
+}
+
+// Init the connection to bitmask
+func Init() (*Bitmask, error) {
+ statusCh := make(chan string, 10)
+ tempdir, err := ioutil.TempDir("", "leap-")
+ if err != nil {
+ return nil, err
+ }
+ bonafide := newBonafide()
+ launch, err := newLauncher()
+ if err != nil {
+ return nil, err
+ }
+ b := Bitmask{tempdir, statusCh, nil, bonafide, launch}
+
+ err = b.StopVPN()
+ if err != nil {
+ return nil, err
+ }
+
+ cert, err := b.bonafide.getCertPem()
+ if err != nil {
+ return nil, err
+ }
+ err = ioutil.WriteFile(b.getCertPemPath(), cert, 0600)
+ if err != nil {
+ return nil, err
+ }
+ err = ioutil.WriteFile(b.getCaCertPath(), caCert, 0600)
+
+ go b.openvpnManagement()
+ return &b, err
+}
+
+// GetStatusCh returns a channel that will recieve VPN status changes
+func (b *Bitmask) GetStatusCh() <-chan string {
+ return b.statusCh
+}
+
+// Close the connection to bitmask
+func (b *Bitmask) Close() {
+ b.StopVPN()
+ err := b.launch.close()
+ if err != nil {
+ log.Printf("There was an error closing the launcher: %v", err)
+ }
+ err = os.RemoveAll(b.tempdir)
+ if err != nil {
+ log.Printf("There was an error removing temp dir: %v", err)
+ }
+}
+
+// Version gets the bitmask version string
+func (b *Bitmask) Version() (string, error) {
+ return "", nil
+}
diff --git a/standalone/status.go b/standalone/status.go
new file mode 100644
index 0000000..5a1ee3d
--- /dev/null
+++ b/standalone/status.go
@@ -0,0 +1,91 @@
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/apparentlymart/go-openvpn-mgmt/openvpn"
+)
+
+const (
+ On = "on"
+ Off = "off"
+ Starting = "starting"
+ Stopping = "stopping"
+ Failed = "failed"
+)
+
+var statusNames = map[string]string{
+ "CONNECTING": Starting,
+ "WAIT": Starting,
+ "AUTH": Starting,
+ "GET_CONFIG": Starting,
+ "ASSIGN_IP": Starting,
+ "ADD_ROUTES": Starting,
+ "CONNECTED": On,
+ "RECONNECTING": Starting,
+ "EXITING": Stopping,
+ "OFF": Off,
+ "FAILED": Off,
+}
+
+func (b *Bitmask) openvpnManagement() {
+ // TODO: we should warn the user on ListenAndServe errors
+ newConnection := func(conn openvpn.IncomingConn) {
+ eventCh := make(chan openvpn.Event, 10)
+ log.Println("New connection into the management")
+ b.managementClient = conn.Open(eventCh)
+ b.managementClient.SetStateEvents(true)
+ b.eventHandler(eventCh)
+ }
+ log.Fatal(openvpn.ListenAndServe(
+ fmt.Sprintf("%s:%s", openvpnManagementAddr, openvpnManagementPort),
+ openvpn.IncomingConnHandlerFunc(newConnection),
+ ))
+}
+
+func (b *Bitmask) eventHandler(eventCh <-chan openvpn.Event) {
+ // TODO: we are reporing only openvpn status, missing firewall status
+ for event := range eventCh {
+ log.Printf("Event: %v", event)
+ stateEvent, ok := event.(*openvpn.StateEvent)
+ if !ok {
+ continue
+ }
+ status, ok := statusNames[stateEvent.NewState()]
+ if ok {
+ b.statusCh <- status
+ }
+ }
+ b.statusCh <- Off
+}
+
+func (b *Bitmask) getOpenvpnState() (string, error) {
+ if b.managementClient == nil {
+ return "", fmt.Errorf("No management connected")
+ }
+ stateEvent, err := b.managementClient.LatestState()
+ if err != nil {
+ return "", err
+ }
+ status, ok := statusNames[stateEvent.NewState()]
+ if !ok {
+ return "", fmt.Errorf("Unkonw status")
+ }
+ return status, nil
+}
diff --git a/standalone/vpn.go b/standalone/vpn.go
new file mode 100644
index 0000000..44fa768
--- /dev/null
+++ b/standalone/vpn.go
@@ -0,0 +1,121 @@
+// Copyright (C) 2018 LEAP
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package bitmask
+
+import (
+ "path"
+ "runtime"
+)
+
+const (
+ openvpnManagementAddr = "127.0.0.1"
+ openvpnManagementPort = "6061"
+)
+
+// StartVPN for provider
+func (b *Bitmask) StartVPN(provider string) error {
+ gateways, err := b.bonafide.getGateways()
+ if err != nil {
+ return err
+ }
+ err = b.launch.firewallStart(gateways)
+ if err != nil {
+ return err
+ }
+
+ arg, err := b.bonafide.getOpenvpnArgs()
+ if err != nil {
+ return err
+ }
+ for _, gw := range gateways {
+ arg = append(arg, "--remote", gw.IPAddress, "443", "tcp4")
+ }
+ certPemPath := b.getCertPemPath()
+ arg = append(arg,
+ "--nobind",
+ "--verb", "1",
+ "--dev", "tun",
+ "--client",
+ "--tls-client",
+ "--remote-cert-tls", "server",
+ "--script-security", "1",
+ "--management-client",
+ "--management", openvpnManagementAddr, openvpnManagementPort,
+ "--ca", b.getCaCertPath(),
+ "--cert", certPemPath,
+ "--key", certPemPath)
+ if runtime.GOOS == "windows" {
+ arg = append(arg, "--log", `C:\bitmask\openvp.log`)
+ }
+ return b.launch.openvpnStart(arg...)
+}
+
+// StopVPN or cancel
+func (b *Bitmask) StopVPN() error {
+ err := b.launch.firewallStop()
+ if err != nil {
+ return err
+ }
+ return b.launch.openvpnStop()
+}
+
+// GetStatus returns the VPN status
+func (b *Bitmask) GetStatus() (string, error) {
+ status, err := b.getOpenvpnState()
+ if err != nil {
+ status = Off
+ }
+ return status, nil
+}
+
+// InstallHelpers into the system
+func (b *Bitmask) InstallHelpers() error {
+ // TODO
+ return nil
+}
+
+// VPNCheck returns if the helpers are installed and up to date and if polkit is running
+func (b *Bitmask) VPNCheck() (helpers bool, priviledge bool, err error) {
+ // TODO
+ return true, true, nil
+}
+
+// ListGateways return the names of the gateways
+func (b *Bitmask) ListGateways(provider string) ([]string, error) {
+ gateways, err := b.bonafide.getGateways()
+ if err != nil {
+ return nil, err
+ }
+ gatewayNames := make([]string, len(gateways))
+ for i, gw := range gateways {
+ gatewayNames[i] = gw.Location
+ }
+ return gatewayNames, nil
+}
+
+// UseGateway selects name as the default gateway
+func (b *Bitmask) UseGateway(name string) error {
+ b.bonafide.setDefaultGateway(name)
+ return nil
+}
+
+func (b *Bitmask) getCertPemPath() string {
+ return path.Join(b.tempdir, "openvpn.pem")
+}
+
+func (b *Bitmask) getCaCertPath() string {
+ return path.Join(b.tempdir, "cacert.pem")
+}