summaryrefslogtreecommitdiff
path: root/bitmask_go
diff options
context:
space:
mode:
authorRuben Pollan <meskio@sindominio.net>2018-06-08 10:26:58 +0200
committerRuben Pollan <meskio@sindominio.net>2018-06-20 12:18:03 +0200
commitfcc7514ec5f1b35068b1033d8ac4278c45043a80 (patch)
tree0ad4d5f4d650ab2a01879e4f31faedf848c39287 /bitmask_go
parent11094e0f58e1f28f5333a91f3c4129b56ad154e6 (diff)
[feat] pure go bitmask vpn implemenation
- Resolves: #42
Diffstat (limited to 'bitmask_go')
-rw-r--r--bitmask_go/bonafide.go87
-rw-r--r--bitmask_go/bonafide_test.go26
-rw-r--r--bitmask_go/launcher_linux.go92
-rw-r--r--bitmask_go/main.go78
-rw-r--r--bitmask_go/status.go91
-rw-r--r--bitmask_go/vpn.go98
6 files changed, 472 insertions, 0 deletions
diff --git a/bitmask_go/bonafide.go b/bitmask_go/bonafide.go
new file mode 100644
index 0000000..25fa302
--- /dev/null
+++ b/bitmask_go/bonafide.go
@@ -0,0 +1,87 @@
+// 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"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+)
+
+const (
+ certAPI = "https://api.black.riseup.net/1/cert"
+)
+
+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-----`)
+)
+
+func getCertPem() ([]byte, error) {
+ certs := x509.NewCertPool()
+ certs.AppendCertsFromPEM(caCert)
+ client := &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tls.Config{
+ RootCAs: certs,
+ },
+ },
+ }
+
+ resp, err := 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)
+}
diff --git a/bitmask_go/bonafide_test.go b/bitmask_go/bonafide_test.go
new file mode 100644
index 0000000..c40c98d
--- /dev/null
+++ b/bitmask_go/bonafide_test.go
@@ -0,0 +1,26 @@
+package bitmask
+
+import (
+ "bytes"
+ "testing"
+)
+
+var (
+ privateKeyHeader = []byte("-----BEGIN RSA PRIVATE KEY-----")
+ certHeader = []byte("-----BEGIN CERTIFICATE-----")
+)
+
+func TestGetCert(t *testing.T) {
+ cert, err := getCertPem()
+ if err != nil {
+ t.Fatal("get_cert returned an error: ", err)
+ }
+
+ if !bytes.Contains(cert, privateKeyHeader) {
+ t.Errorf("No private key present: \n%q", cert)
+ }
+
+ if !bytes.Equal(cert, certHeader) {
+ t.Errorf("No cert present: \n%q", cert)
+ }
+}
diff --git a/bitmask_go/launcher_linux.go b/bitmask_go/launcher_linux.go
new file mode 100644
index 0000000..05398a6
--- /dev/null
+++ b/bitmask_go/launcher_linux.go
@@ -0,0 +1,92 @@
+// +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",
+}
+
+func openvpnStart(flags ...string) error {
+ log.Println("openvpn start: ", flags)
+ arg := []string{"openvpn", "start", getOpenvpnPath()}
+ arg = append(arg, flags...)
+ // TODO: check errors somehow instead of fire and forget
+ go runBitmaskRoot(arg...)
+ return nil
+}
+
+func openvpnStop() error {
+ log.Println("openvpn stop")
+ return runBitmaskRoot("openvpn", "stop")
+}
+
+func firewallStart(gateways []string) error {
+ log.Println("firewall start")
+ arg := []string{"firewall", "start"}
+ arg = append(arg, gateways...)
+ return runBitmaskRoot(arg...)
+}
+
+func firewallStop() error {
+ log.Println("firewall stop")
+ return runBitmaskRoot("firewall", "stop")
+}
+
+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/bitmask_go/main.go b/bitmask_go/main.go
new file mode 100644
index 0000000..49f803a
--- /dev/null
+++ b/bitmask_go/main.go
@@ -0,0 +1,78 @@
+// 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
+}
+
+// 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
+ }
+ b := Bitmask{tempdir, statusCh, nil}
+
+ err = b.StopVPN()
+ if err != nil {
+ return nil, err
+ }
+
+ cert, err := 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 := 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/bitmask_go/status.go b/bitmask_go/status.go
new file mode 100644
index 0000000..5a1ee3d
--- /dev/null
+++ b/bitmask_go/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/bitmask_go/vpn.go b/bitmask_go/vpn.go
new file mode 100644
index 0000000..09adcf0
--- /dev/null
+++ b/bitmask_go/vpn.go
@@ -0,0 +1,98 @@
+// 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"
+)
+
+const (
+ openvpnManagementAddr = "127.0.0.1"
+ openvpnManagementPort = "6061"
+)
+
+var gateways = []string{
+ "5.79.86.180",
+ "199.58.81.145",
+ "198.252.153.28",
+}
+
+// StartVPN for provider
+func (b *Bitmask) StartVPN(provider string) error {
+ // TODO: openvpn args are hardcoded
+ err := firewallStart(gateways)
+ if err != nil {
+ return err
+ }
+
+ arg := []string{"--nobind", "--verb", "1"}
+ for _, gw := range gateways {
+ arg = append(arg, "--remote", gw, "443", "tcp4")
+ }
+ certPemPath := b.getCertPemPath()
+ arg = append(arg, "--client", "--tls-client", "--remote-cert-tls", "server", "--tls-cipher", "DHE-RSA-AES128-SHA", "--cipher", "AES-128-CBC", "--tun-ipv6", "--auth", "SHA1", "--keepalive", "10 30", "--management-client", "--management", openvpnManagementAddr+" "+openvpnManagementPort, "--ca", b.getCaCertPath(), "--cert", certPemPath, "--key", certPemPath)
+ return openvpnStart(arg...)
+}
+
+// StopVPN or cancel
+func (b *Bitmask) StopVPN() error {
+ err := firewallStop()
+ if err != nil {
+ return err
+ }
+ return 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) {
+ // TODO
+ return []string{}, nil
+}
+
+// UseGateway selects name as the default gateway
+func (b *Bitmask) UseGateway(name string) error {
+ // TODO
+ 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")
+}