From 5d65d89e01105bc1b37995175cd9336ea2c737e0 Mon Sep 17 00:00:00 2001
From: "Kali Kaneko (leap communications)" <kali@leap.se>
Date: Fri, 28 Jun 2019 17:01:41 +0200
Subject: [PATCH] [feat] add vendorize script to generate config

---
 Makefile                      |  3 ++
 branding/check-ca-crt.py      | 46 ++++++++++++++++
 branding/config.go.tmpl       | 32 +++++++++++
 branding/config/riseup-ca.crt | 32 +++++++++++
 branding/config/vendor.conf   | 24 +++++++++
 branding/vendorize.py         | 99 +++++++++++++++++++++++++++++++++++
 cmd/bitmask-vpn/main.go       |  2 +
 pkg/config/config.go          | 24 +++++++--
 8 files changed, 259 insertions(+), 3 deletions(-)
 create mode 100755 branding/check-ca-crt.py
 create mode 100644 branding/config.go.tmpl
 create mode 100644 branding/config/riseup-ca.crt
 create mode 100644 branding/config/vendor.conf
 create mode 100755 branding/vendorize.py

diff --git a/Makefile b/Makefile
index 9b1762e7..8fba6720 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,9 @@ get:
 	go get -tags $(TAGS) ./...
 	go get -tags "$(TAGS) bitmaskd" ./...
 
+generate:
+	go generate cmd/bitmask-vpn/main.go
+
 build: $(foreach path,$(wildcard cmd/*),build_$(patsubst cmd/%,%,$(path)))
 
 build_%:
diff --git a/branding/check-ca-crt.py b/branding/check-ca-crt.py
new file mode 100755
index 00000000..64624678
--- /dev/null
+++ b/branding/check-ca-crt.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+import re
+import sys
+import urllib.request
+
+SCRIPT_NAME = 'check-ca-crt.py'
+
+USAGE = '''Check that the stored provider CA matches the one announced online.
+Usage: {name} <provider> <uri>
+
+Example: {name} riseup black.riseup.net'''.format(name=SCRIPT_NAME)
+
+
+def getLocalCert(provider):
+    sanitized = re.sub(r'[^\w\s-]', '', provider).strip().lower()
+    with open('config/{provider}-ca.crt'.format(provider=sanitized)) as crt:
+        return crt.read().strip()
+
+
+def getRemoteCert(uri):
+    fp = urllib.request.urlopen('https://' + uri + '/ca.crt')
+    remote_cert = fp.read().decode('utf-8').strip()
+    fp.close()
+    return remote_cert
+
+
+if __name__ == '__main__':
+
+    if len(sys.argv) != 3:
+        print('[!] Not enough arguments')
+        print(USAGE)
+        sys.exit(1)
+
+    provider = sys.argv[1]
+    uri = sys.argv[2]
+
+    local = getLocalCert(provider)
+    remote = getRemoteCert(uri)
+
+    try:
+        assert local == remote
+    except AssertionError:
+        print('[!] ERROR: remote and local CA certs do not match')
+        sys.exit(1)
+    else:
+        print('OK')
diff --git a/branding/config.go.tmpl b/branding/config.go.tmpl
new file mode 100644
index 00000000..c3a9a330
--- /dev/null
+++ b/branding/config.go.tmpl
@@ -0,0 +1,32 @@
+/*
+   DO NOT EDIT --------------------------------------------------
+
+   This file has been automatically generated by `go generate`.
+   Any changes will be overriden.
+
+   DO NOT EDIT --------------------------------------------------
+*/
+
+package config
+
+/* All these constants are defined in the vendor.conf file
+*/
+const (
+	Provider        = "$providerURL"
+	ApplicationName = "$applicationName"
+	BinaryName      = "$binaryName"
+	DonateURL       = "$donateURL"
+	HelpURL         = "$helpURL"
+	TosURL          = "$tosURL"
+	APIURL          = "$apiURL"
+	GeolocationAPI  = "$geolocationAPI"
+)
+
+/*
+
+CaCert : a string containing a representation of the provider CA, used to
+        sign the webapp and openvpn certificates. should be placed in
+        config/[provider]-ca.crt
+
+*/
+var CaCert = []byte(`$caCertString`)
diff --git a/branding/config/riseup-ca.crt b/branding/config/riseup-ca.crt
new file mode 100644
index 00000000..cbec39c6
--- /dev/null
+++ b/branding/config/riseup-ca.crt
@@ -0,0 +1,32 @@
+-----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-----
diff --git a/branding/config/vendor.conf b/branding/config/vendor.conf
new file mode 100644
index 00000000..ee5aa364
--- /dev/null
+++ b/branding/config/vendor.conf
@@ -0,0 +1,24 @@
+[default]
+
+provider = riseup
+
+
+[riseup]
+
+name                = Riseup
+applicationName     = RiseupVPN
+binaryName          = riseup-vpn 
+providerURL         = riseup.net
+tosURL              = https://riseup.net/tos
+helpURL             = https://riseup.net/support
+donateURL           = https://riseup.net/donate
+apiURL              = https://api.black.riseup.net/
+geolocationAPI      = https://api.black.riseup.net:9001/json
+
+
+[calyx]
+
+name                = Calyx
+applicationName     = CalyxVPN
+binaryName          = calyx-vpn
+apiURL              = https://calyx.org
diff --git a/branding/vendorize.py b/branding/vendorize.py
new file mode 100755
index 00000000..46cc1e69
--- /dev/null
+++ b/branding/vendorize.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+from string import Template
+import configparser
+
+OUTFILE = 'config.go'
+INFILE = 'config.go.tmpl'
+CONFIGFILE = 'config/vendor.conf'
+SCRIPT_NAME = 'vendorize'
+
+
+def getProviderData(config):
+    default = config['default']['provider']
+    print("[+] Configured provider:", default)
+
+    c = config[default]
+    d = dict()
+
+    keys = ('name', 'applicationName', 'binaryName',
+            'providerURL', 'tosURL', 'helpURL',
+            'donateURL', 'apiURL', 'geolocationAPI', 'caCertString')
+
+    for value in keys:
+        d[value] = c.get(value)
+
+    return d
+
+
+def addCaData(data, configfile):
+    provider = data.get('name').lower()
+    folder, f = os.path.split(configfile)
+    caFile = os.path.join(folder, provider + '-ca.crt')
+    if not os.path.isfile(caFile):
+        bail('[!] Cannot find CA file in {path}'.format(path=caFile))
+    with open(caFile) as ca:
+        data['caCertString'] = ca.read().strip()
+
+
+def writeOutput(data, infile, outfile):
+
+    with open(infile) as infile:
+        s = Template(infile.read())
+
+    with open(outfile, 'w') as outf:
+        outf.write(s.substitute(data))
+
+
+def bail(msg=None):
+    if not msg:
+        print('Usage: {scriptname}.py <template> <config> <output>'.format(
+            scriptname=SCRIPT_NAME))
+    else:
+        print(msg)
+    sys.exit(1)
+
+
+if __name__ == "__main__":
+    infile = outfile = ""
+
+    if len(sys.argv) > 4:
+        bail()
+
+    elif len(sys.argv) == 1:
+        infile = INFILE
+        outfile = OUTFILE
+        configfile = CONFIGFILE
+    else:
+        try:
+            infile = sys.argv[1]
+            configfile = sys.argv[2]
+            outfile = sys.argv[3]
+        except IndexError:
+            bail()
+
+    if not os.path.isfile(infile):
+        bail('[!] Cannot find template in {path}'.format(
+            path=os.path.abspath(infile)))
+    elif not os.path.isfile(configfile):
+        bail('[!] Cannot find config in {path}'.format(
+            path=os.path.abspath(configfile)))
+    else:
+        print('[+] Using {path} as template'.format(
+            path=os.path.abspath(infile)))
+        print('[+] Using {path} as config'.format(
+            path=os.path.abspath(configfile)))
+
+    config = configparser.ConfigParser()
+    config.read(configfile)
+
+    data = getProviderData(config)
+    addCaData(data, configfile)
+    writeOutput(data, infile, outfile)
+
+    print('[+] Wrote configuration for {provider} to {outf}'.format(
+        provider=data.get('name'),
+        outf=os.path.abspath(outfile)))
diff --git a/cmd/bitmask-vpn/main.go b/cmd/bitmask-vpn/main.go
index 77ace31a..fa1daf3a 100644
--- a/cmd/bitmask-vpn/main.go
+++ b/cmd/bitmask-vpn/main.go
@@ -15,6 +15,8 @@
 
 package main
 
+//go:generate ../../branding/vendorize.py ../../branding/config.go.tmpl ../../branding/config/vendor.conf ../../pkg/config/config.go
+
 import (
 	"flag"
 	"fmt"
diff --git a/pkg/config/config.go b/pkg/config/config.go
index fed3cc93..29f724a7 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,16 +1,34 @@
+/*
+   DO NOT EDIT --------------------------------------------------
+
+   This file has been automatically generated by `go generate`.
+   Any changes will be overriden.
+
+   DO NOT EDIT --------------------------------------------------
+*/
+
 package config
 
+/* All these constants are defined in the vendor.conf file
+*/
 const (
+	Provider        = "riseup.net"
 	ApplicationName = "RiseupVPN"
 	BinaryName      = "riseup-vpn"
-	Provider        = "riseup.net"
-	DonateURL       = "https://riseup.net/vpn/donate"
-	HelpURL         = "https://riseup.net/vpn/support"
+	DonateURL       = "https://riseup.net/donate"
+	HelpURL         = "https://riseup.net/support"
 	TosURL          = "https://riseup.net/tos"
 	APIURL          = "https://api.black.riseup.net/"
 	GeolocationAPI  = "https://api.black.riseup.net:9001/json"
 )
 
+/*
+
+CaCert : a string containing a representation of the provider CA, used to
+        sign the webapp and openvpn certificates. should be placed in
+        config/[provider]/ca.crt
+
+*/
 var CaCert = []byte(`-----BEGIN CERTIFICATE-----
 MIIFjTCCA3WgAwIBAgIBATANBgkqhkiG9w0BAQ0FADBZMRgwFgYDVQQKDA9SaXNl
 dXAgTmV0d29ya3MxGzAZBgNVBAsMEmh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UE
-- 
GitLab