Skip to content
Snippets Groups Projects
Verified Commit fcc7514e authored by meskio's avatar meskio :tent:
Browse files

[feat] pure go bitmask vpn implemenation

- Resolves: #42
parent 11094e0f
No related branches found
No related tags found
No related merge requests found
.PHONY: all get build icon locales generate_locales clean
.PHONY: all get build build_go icon locales generate_locales clean
TAGS ?= gtk_3_18
all: icon locales get build
get:
go get -tags 'gtk_3_18' .
go get -tags $(TAGS) . ./bitmask_go
build:
go build -tags 'gtk_3_18' -ldflags "-X main.version=`git describe --tags`"
go build -tags $(TAGS) -ldflags "-X main.version=`git describe --tags`"
test:
go test -tags 'gtk_3_18' ./...
go test -tags $(TAGS) ./...
build_go:
go build -tags "$(TAGS) bitmask_go" -ldflags "-X main.version=`git describe --tags`"
clean:
make -C icon clean
......
// 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
type Bitmask interface {
GetStatusCh() <-chan string
Close()
Version() (string, error)
StartVPN(provider string) error
StopVPN() error
GetStatus() (string, error)
InstallHelpers() error
VPNCheck() (helpers bool, priviledge bool, err error)
ListGateways(provider string) ([]string, error)
UseGateway(name string) error
}
// +build bitmask_go
// 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 main
import (
"0xacab.org/leap/bitmask-systray/bitmask"
bitmask_go "0xacab.org/leap/bitmask-systray/bitmask_go"
)
func initBitmask() (bitmask.Bitmask, error) {
return bitmask_go.Init()
}
// 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)
}
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)
}
}
// +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
}
// 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
}
// 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
}
// 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")
}
// +build !bitmask_go
// 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 main
import (
"0xacab.org/leap/bitmask-systray/bitmask"
bitmaskd "0xacab.org/leap/bitmask-systray/bitmaskd"
)
func initBitmask() (bitmask.Bitmask, error) {
return bitmaskd.Init()
}
File moved
......@@ -24,6 +24,8 @@ import (
"net/http"
"path"
"time"
"0xacab.org/leap/bitmask-systray/bitmask"
)
const (
......@@ -62,7 +64,7 @@ func Init() (*Bitmask, error) {
}
// GetStatusCh returns a channel that will recieve VPN status changes
func (b *Bitmask) GetStatusCh() chan string {
func (b *Bitmask) GetStatusCh() <-chan string {
return b.statusCh
}
......@@ -148,7 +150,7 @@ func parseResponse(resJSON []byte) (interface{}, error) {
func getToken() (string, error) {
var err error
path := path.Join(ConfigPath, "authtoken")
path := path.Join(bitmask.ConfigPath, "authtoken")
for i := 0; i < 30; i++ {
b, err := ioutil.ReadFile(path)
if err == nil {
......
File moved
......@@ -62,7 +62,7 @@ func main() {
notify := newNotificator(conf)
b, err := bitmask.Init()
b, err := initBitmask()
if err != nil {
log.Print(err)
notify.bitmaskNotRunning()
......@@ -74,7 +74,7 @@ func main() {
run(b, conf, notify)
}
func checkAndStartBitmask(b *bitmask.Bitmask, notify *notificator, conf *systrayConfig) {
func checkAndStartBitmask(b bitmask.Bitmask, notify *notificator, conf *systrayConfig) {
err := checkAndInstallHelpers(b, notify)
if err != nil {
log.Printf("Is bitmask running? %v", err)
......@@ -87,7 +87,7 @@ func checkAndStartBitmask(b *bitmask.Bitmask, notify *notificator, conf *systray
}
}
func checkAndInstallHelpers(b *bitmask.Bitmask, notify *notificator) error {
func checkAndInstallHelpers(b bitmask.Bitmask, notify *notificator) error {
helpers, priviledge, err := b.VPNCheck()
if (err != nil && err.Error() == "nopolkit") || (err == nil && !priviledge) {
log.Printf("No polkit found")
......@@ -106,7 +106,7 @@ func checkAndInstallHelpers(b *bitmask.Bitmask, notify *notificator) error {
return nil
}
func maybeStartVPN(b *bitmask.Bitmask, conf *systrayConfig) error {
func maybeStartVPN(b bitmask.Bitmask, conf *systrayConfig) error {
if conf.UserStoppedVPN {
return nil
}
......
......@@ -27,7 +27,7 @@ import (
)
type bmTray struct {
bm *bitmask.Bitmask
bm bitmask.Bitmask
conf *systrayConfig
notify *notificator
waitCh chan bool
......@@ -44,7 +44,7 @@ type gatewayTray struct {
name string
}
func run(bm *bitmask.Bitmask, conf *systrayConfig, notify *notificator) {
func run(bm bitmask.Bitmask, conf *systrayConfig, notify *notificator) {
bt := bmTray{bm: bm, conf: conf, notify: notify}
systray.Run(bt.onReady, bt.onExit)
}
......@@ -117,13 +117,13 @@ func (bt *bmTray) onReady() {
open.Run("https://riseup.net/vpn/donate")
case <-mAbout.ClickedCh:
bitmaskVersion, err := bt.bm.Version()
versionStr := version
if err != nil {
log.Printf("Error getting version: %v", err)
bt.notify.about(version)
} else {
versionStr := fmt.Sprintf("%s (bitmaskd %s)", version, bitmaskVersion)
bt.notify.about(versionStr)
} else if bitmaskVersion != "" {
versionStr = fmt.Sprintf("%s (bitmaskd %s)", version, bitmaskVersion)
}
bt.notify.about(versionStr)
case <-mQuit.ClickedCh:
systray.Quit()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment