diff --git a/.gitignore b/.gitignore index 4c21651c3266e977c2fc612e86d13b8bf2662a5f..acc2769980c098fdd151e333138957cee58f3175 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +__pycache__ +build +dist /bitmask-vpn cmd/bitmask-vpn/bitmask-vpn /bitmask-helper diff --git a/Makefile b/Makefile index 6f54daf7536e34c001f011818c583402d3e4b402..8f4d836c55d4ac24163c34f45a8047fe0a7b910a 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ TAGS ?= gtk_3_18 PROVIDER ?= $(shell grep ^'provider =' branding/config/vendor.conf | cut -d '=' -f 2 | tr -d "[:space:]") PROVIDER_CONFIG ?= branding/config/vendor.conf DEFAULT_PROVIDER = branding/assets/default/ +VERSION ?= $(shell git describe) all: icon locales get build @@ -22,13 +23,35 @@ endif cd branding/assets && ln -s ${PROVIDER} default prepare: generate relink_default + mkdir -p build/${PROVIDER}/bin/ branding/scripts/check-ca-crt.py ${PROVIDER} ${PROVIDER_CONFIG} +prepare_win: + mkdir -p build/${PROVIDER}/windows/ + cp -r branding/templates/windows build/${PROVIDER} + VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} branding/scripts/generate-win.py build/${PROVIDER}/windows/data.json + cd build/${PROVIDER}/windows && python3 generate.py + # TODO create build/PROVIDER/assets/ + # TODO create build/PROVIDER/staging/ + +prepare_osx: + echo "osx..." + +prepare_snap: + echo "snap..." + +prepare_debian: + echo "debian..." + +prepare_all: prepare prepare_win prepare_osx prepare_snap + build: $(foreach path,$(wildcard cmd/*),build_$(patsubst cmd/%,%,$(path))) build_%: go build -tags $(TAGS) -ldflags "-X main.version=`git describe --tags`" -o $* ./cmd/$* strip $* + mkdir -p build/bin + mv $* build/bin/ test: go test -tags "integration $(TAGS)" ./... @@ -41,7 +64,7 @@ build_win: clean: make -C icon clean - rm bitmask-vpn + rm build/bitmask-vpn icon: make -C icon diff --git a/branding/config/vendor.conf b/branding/config/vendor.conf index 12fca4e51e540998a3a22afe48826f802ad9519c..f0f42c18d0f75f248d14e68e03e9ac14dfeb6a06 100644 --- a/branding/config/vendor.conf +++ b/branding/config/vendor.conf @@ -13,6 +13,7 @@ providerURL = riseup.net apiURL = https://api.black.riseup.net/ caURL = https://black.riseup.net/ca.crt +infoURL = https://riseup.net/vpn tosURL = https://riseup.net/tos helpURL = https://riseup.net/support @@ -27,11 +28,15 @@ donateURL = https://riseup.net/donate name = Calyx applicationName = CalyxVPN binaryName = calyx-vpn + providerURL = https://calyx.net -tosURL = https://calyx.net/tos -helpURL = https://calyx.net/support apiURL = https://api.calyx.net:4430/ caURL = https://calyx.net/ca.crt + +infoURL = https://calyx.net/ +tosURL = https://calyx.net/tos +helpURL = https://calyx.net/support + geolocationAPI = https://api.black.riseup.net:9001/json askForDonations = true diff --git a/branding/scripts/TODO b/branding/scripts/TODO new file mode 100644 index 0000000000000000000000000000000000000000..95ca3450156f5198cd9ba5494cfa6aa4dfff5eb2 --- /dev/null +++ b/branding/scripts/TODO @@ -0,0 +1,2 @@ +-[ ] sanity check scrit +-[ ] assets convertion script diff --git a/branding/scripts/__init__.py b/branding/scripts/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/branding/scripts/generate-win.py b/branding/scripts/generate-win.py new file mode 100755 index 0000000000000000000000000000000000000000..fb15f22a161005c4919cd1265cf2009a3bd1e49e --- /dev/null +++ b/branding/scripts/generate-win.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import json +import os +import sys + +import configparser + +from provider import getDefaultProvider +from provider import getProviderData + + +VERSION = os.environ.get('VERSION', 'unknown') + + +def writeOutput(data, outfile): + + with open(outfile, 'w') as outf: + outf.write(json.dumps(data)) + + +if __name__ == "__main__": + env_provider_conf = os.environ.get('PROVIDER_CONFIG') + if env_provider_conf: + if os.path.isfile(env_provider_conf): + print("[+] Overriding provider config per " + "PROVIDER_CONFIG variable") + configfile = env_provider_conf + + config = configparser.ConfigParser() + config.read(configfile) + provider = getDefaultProvider(config) + data = getProviderData(provider, config) + + if len(sys.argv) != 2: + print('Usage: generate-win.py <output_file>') + sys.exit(1) + + outputf = sys.argv[1] + + data['applicationNameLower'] = data.get('applicationName').lower() + data['URL'] = data.get('infoURL') + data['version'] = VERSION + writeOutput(data, outputf) diff --git a/branding/scripts/provider.py b/branding/scripts/provider.py new file mode 100644 index 0000000000000000000000000000000000000000..867007e2f28252b1b87b8fdb97aff85dcc807743 --- /dev/null +++ b/branding/scripts/provider.py @@ -0,0 +1,32 @@ +import datetime +import os + + +def getDefaultProvider(config): + provider = os.environ.get('PROVIDER') + if provider: + print('[+] Got provider {} from environemnt'.format(provider)) + else: + print('[+] Using default provider from config file') + provider = config['default']['provider'] + return provider + + +def getProviderData(provider, config): + print("[+] Configured provider:", provider) + + c = config[provider] + d = dict() + + keys = ('name', 'applicationName', 'binaryName', + 'providerURL', 'tosURL', 'helpURL', + 'donateURL', 'apiURL', 'geolocationAPI', 'caCertString') + + for value in keys: + d[value] = c.get(value) + + d['timeStamp'] = '{:%Y-%m-%d %H:%M:%S}'.format( + datetime.datetime.now()) + + return d + diff --git a/branding/scripts/vendorize.py b/branding/scripts/vendorize.py index dc1995252f4108c1228fef87f2fef72b3d192f5f..59edae08f0c6f4567ce6b30f710ad4a87a1f7f2c 100755 --- a/branding/scripts/vendorize.py +++ b/branding/scripts/vendorize.py @@ -1,47 +1,20 @@ #!/usr/bin/env python3 -import datetime import os import sys from string import Template import configparser +from provider import getDefaultProvider +from provider import getProviderData + OUTFILE = 'config.go' -INFILE = 'config.go.tmpl' -CONFIGFILE = 'config/vendor.conf' +INFILE = '../templates/golang/config.go' +CONFIGFILE = '../config/vendor.conf' SCRIPT_NAME = 'vendorize' -def getDefaultProvider(config): - provider = os.environ.get('PROVIDER') - if provider: - print('[+] Got provider {} from environemnt'.format(provider)) - else: - print('[+] Using default provider from config file') - provider = config['default']['provider'] - return provider - - -def getProviderData(provider, config): - print("[+] Configured provider:", provider) - - c = config[provider] - d = dict() - - keys = ('name', 'applicationName', 'binaryName', - 'providerURL', 'tosURL', 'helpURL', - 'donateURL', 'apiURL', 'geolocationAPI', 'caCertString') - - for value in keys: - d[value] = c.get(value) - - d['timeStamp'] = '{:%Y-%m-%d %H:%M:%S}'.format( - datetime.datetime.now()) - - return d - - def addCaData(data, configfile): provider = data.get('name').lower() folder, f = os.path.split(configfile) @@ -91,7 +64,8 @@ if __name__ == "__main__": env_provider_conf = os.environ.get('PROVIDER_CONFIG') if env_provider_conf: if os.path.isfile(env_provider_conf): - print("[+] Overriding provider config per PROVIDER_CONFIG variable") + print("[+] Overriding provider config per " + "PROVIDER_CONFIG variable") configfile = env_provider_conf if not os.path.isfile(infile): diff --git a/branding/templates/windows/generate.py b/branding/templates/windows/generate.py new file mode 100755 index 0000000000000000000000000000000000000000..427b7a8c4b5235de50ff765d8fad8eba0350e2cc --- /dev/null +++ b/branding/templates/windows/generate.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +""" +generate.py + +Generate a NSI installer for a given provider. +""" + +import json +import os +from string import Template + + +TEMPLATE = 'template.nsi' + + +def get_files(which): + files = "\n" + if which == 'install': + action = "File " + elif which == 'uninstall': + action = "Delete $INSTDIR\\" + else: + action = "" + + # TODO get relative path + for item in open('payload/' + which).readlines(): + files += " {action}{item}".format( + action=action, item=item) + return files + + +here = os.path.split(os.path.realpath(__file__))[0] +data = json.load(open(os.path.join(here, 'data.json'))) +data['extra_install_files'] = get_files('install') +data['extra_uninstall_files'] = get_files('uninstall') + +import pprint +pprint.pprint(data) + +INSTALLER = data['applicationName'] + '-installer.nsi' + + +template = Template(open(TEMPLATE).read()) +with open(INSTALLER, 'w') as output: + output.write(template.safe_substitute(data)) + +print("[+] NSIS installer script written to {path}".format(path=INSTALLER)) diff --git a/branding/templates/windows/payload/install b/branding/templates/windows/payload/install new file mode 100755 index 0000000000000000000000000000000000000000..da9a19506bd7f78094afd81da827ed1ffaf4f619 --- /dev/null +++ b/branding/templates/windows/payload/install @@ -0,0 +1,9 @@ +..\staging\bitmask_helper.exe +..\staging\bitmask-vpn.exe +..\staging\libcrypto-1_1-x64.dll +..\staging\liblzo2-2.dll +..\staging\libpkcs11-helper-1.dll +..\staging\libssl-1_1-x64.dll +..\staging\padlock.dll +..\staging\openvpn\openvpn.exe +..\staging\openvpn\tap-windows.exe diff --git a/branding/templates/windows/payload/uninstall b/branding/templates/windows/payload/uninstall new file mode 100755 index 0000000000000000000000000000000000000000..7c7df24c3c626344328b08a402843b68005a0ed9 --- /dev/null +++ b/branding/templates/windows/payload/uninstall @@ -0,0 +1,13 @@ +icon.ico +openssl.exe +openvpn.exe +ssleay32.dll +libeay32.dll +liblzo2-2.dll +libpkcs11-helper-1.dll +libcrypto-1_1-x64.dll +libssl-1_1-x64.dll +padlock.dll +bitmask_helper.exe +bitmask-vpn.exe +tap-windows.exe diff --git a/branding/templates/windows/sign.py b/branding/templates/windows/sign.py new file mode 100644 index 0000000000000000000000000000000000000000..5b6b2c6e702f78b98af828cb872c97e0420f3604 --- /dev/null +++ b/branding/templates/windows/sign.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +""" +This script is expected to be called from the main makefile, that should pass +the content of the WIN_CERT_PASS variable as the second argument. + +Just make sure that $GOPATH is properly configured. +""" +import subprocess +import os +import sys + +WIN_CERT_PATH = sys.argv[1] +WIN_CERT_PASS = sys.argv[2] +SIGNTOOL = "signtool" + +GOPATH = os.environ.get('GOPATH') +VERSION = subprocess.run( + 'git -C ' + GOPATH + + '\\src\\0xacab.org\\leap\\bitmask-vpn describe --tags', + stdout=subprocess.PIPE).stdout.strip() + +installer = "RiseupVPN-" + str(VERSION, 'utf-8') + '.exe' +target = str(os.path.join(os.path.abspath('.'), 'dist', installer)) +cmd = [SIGNTOOL, "sign", "/f", WIN_CERT_PATH, "/p", WIN_CERT_PASS, target] +subprocess.run(cmd) diff --git a/branding/templates/windows/template.nsi b/branding/templates/windows/template.nsi new file mode 100755 index 0000000000000000000000000000000000000000..f644b892b8174688966924884ce8ec63e4e4278a --- /dev/null +++ b/branding/templates/windows/template.nsi @@ -0,0 +1,130 @@ +SetCompressor /SOLID lzma + +!define PRODUCT_PUBLISHER "LEAP Encryption Access Project" +!include "MUI2.nsh" + +Name "$applicationName" +Outfile "..\bin\$applicationName-$version.exe" +;TODO make the installdir configurable - and set it in the registry. +InstallDir "C:\Program Files\$applicationName\" +RequestExecutionLevel admin + +!include FileFunc.nsh +!insertmacro GetParameters +!insertmacro GetOptions + +;Macros + +!macro SelectByParameter SECT PARAMETER DEFAULT + ${GetOptions} $R0 "/${PARAMETER}=" $0 + ${If} ${DEFAULT} == 0 + ${If} $0 == 1 + !insertmacro SelectSection ${SECT} + ${EndIf} + ${Else} + ${If} $0 != 0 + !insertmacro SelectSection ${SECT} + ${EndIf} + ${EndIf} +!macroend + + + +!define BITMAP_FILE riseupvpn.bmp + +!define MUI_ICON "..\assets\$applicationNameLower.ico" +!define MUI_UNICON "..\assets\$applicationNameLower.ico" + +!define MUI_WELCOMEPAGE_TITLE "$applicationName" +!define MUI_WELCOMEPAGE_TEXT "This will install $applicationName in your computer. $applicationName is a simple, fast and secure VPN Client, powered by Bitmask. \n This VPN service is run by donations from people like you." +#!define MUI_WELCOMEFINISHPAGE_BITMAP "riseup.png" + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + + + +Section "InstallFiles" + ; first we try to delete the systray, locked by the app. + ClearErrors + Delete 'C:\Program Files\$applicationName\bitmask-vpn.exe' + IfErrors 0 noError + + ; Error handling + MessageBox MB_OK|MB_ICONEXCLAMATION "$applicationName is Running. Please close it, and then run this installer again." + Abort + + noError: + ExecShellWait "runas" "$INSTDIR\nssm.exe" 'stop $applicationNameLower-helper' + ExecShellWait "runas" "$INSTDIR\nssm.exe" 'remove $applicationNameLower-helper confirm' + + SetOutPath $INSTDIR + WriteUninstaller $INSTDIR\uninstall.exe + + ; Add ourselves to Add/remove programs + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayName" "$applicationName" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "InstallLocation" "$INSTDIR" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayIcon" "$INSTDIR\icon.ico" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "Readme" "$INSTDIR\readme.txt" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayVersion" "$version" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "Publisher" "LEAP Encryption Access Project" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "NoRepair" 1 + + ;Start Menu + createDirectory "$SMPROGRAMS\$applicationName\" + createShortCut "$SMPROGRAMS\$applicationName\$applicationName.lnk" "$INSTDIR\bitmask-vpn.exe" "" "$INSTDIR\icon.ico" + + File "readme.txt" + File "..\staging\nssm.exe" + File "/oname=icon.ico" "..\assets\$applicationNameLower.ico" + + $extra_install_files + +SectionEnd + +Section "InstallService" + ; Easy service management thanks to nssm + ExecWait '"$INSTDIR\nssm.exe" install $applicationNameLower-helper "$INSTDIR\bitmask_helper.exe"' + ExecWait '"$INSTDIR\nssm.exe" set $applicationNameLower-helper AppDirectory "$INSTDIR"' + ExecWait '"$INSTDIR\nssm.exe" start $applicationNameLower-helper' +SectionEnd + +Section /o "TAP Virtual Ethernet Adapter" SecTAP + ; Adapted from the windows nsis installer from OpenVPN (openvpn-build repo). + DetailPrint "Installing TAP (may need confirmation)..." + ; The /S flag make it "silent", remove it if you want it explicit + ExecWait '"$INSTDIR\tap-windows.exe" /S /SELECT_UTILITIES=1' + Pop $R0 # return value/error/timeout + WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$applicationName" "tap" "installed" + DetailPrint "TAP installed!" +SectionEnd + +Section "Uninstall" + ExecShellWait "runas" "$INSTDIR\nssm.exe" 'stop $applicationNameLower-helper' + ExecShellWait "runas" "$INSTDIR\nssm.exe" 'remove $applicationNameLower-helper confirm' + + Delete $INSTDIR\readme.txt + Delete $INSTDIR\nssm.exe + Delete $INSTDIR\helper.log + Delete "$SMPROGRAMS\$applicationName\$applicationName.lnk" + RMDir "$SMPROGRAMS\$applicationName\" + + $extra_uninstall_files + + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" + ; uninstaller must be always the last thing to remove + Delete $INSTDIR\uninstall.exe + RMDir $INSTDIR +SectionEnd + +Function .onInit + !insertmacro SelectByParameter ${SecTAP} SELECT_TAP 1 +FunctionEnd + +;---------------------------------------- +;Languages + +!insertmacro MUI_LANGUAGE "English" diff --git a/branding/templates/windows/tools/README-cert.txt b/branding/templates/windows/tools/README-cert.txt new file mode 100644 index 0000000000000000000000000000000000000000..e532997bc1618c710fbcc89c886db986c7b8d733 --- /dev/null +++ b/branding/templates/windows/tools/README-cert.txt @@ -0,0 +1 @@ +openssl pkcs12 -inkey privatekey.pem -in signing_cert.pem -export -out LEAP.pfx diff --git a/branding/templates/windows/tools/README-signtool.txt b/branding/templates/windows/tools/README-signtool.txt new file mode 100644 index 0000000000000000000000000000000000000000..51ebbf56146f8f7038d7aed84eb8ab3814cdc2cc --- /dev/null +++ b/branding/templates/windows/tools/README-signtool.txt @@ -0,0 +1,17 @@ +Source: https://stackoverflow.com/questions/31869552/how-to-install-signtool-exe-for-windows-10 +----------------------------------------------------------------------------------------------- + +If you only want SignTool and really want to minimize the install, here is a way that I just reverse-engineered my way to: + +Download the .iso file from https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk (current download link is http://go.microsoft.com/fwlink/p/?LinkID=2022797) The .exe download will not work, since it's an online installer that pulls down its dependencies at runtime. +Unpack the .iso with a tool such as 7-zip. +Install the Installers/Windows SDK Signing Tools-x86_en-us.msi file - it's only 388 KiB large. For reference, it pulls in its files from the following .cab files, so these are also needed for a standalone install: +4c3ef4b2b1dc72149f979f4243d2accf.cab (339 KiB) + 685f3d4691f444bc382762d603a99afc.cab (1002 KiB) + e5c4b31ff9997ac5603f4f28cd7df602.cab (389 KiB) + e98fa5eb5fee6ce17a7a69d585870b7c.cab (1.2 MiB) + There we go - you will now have the signtool.exe file and companions in C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64 (replace x64 with x86, arm or arm64 if you need it for another CPU architecture.) + +It is also possible to commit signtool.exe and the other files from this folder into your version control repository if want to use it in e.g. CI scenarios. I have tried and it seems to work fine. + +(All files are probably not necessary since there are also some other .exe tools in this folder that might be responsible for these dependencies, but I am not sure which ones could be removed to make the set of files even smaller. Someone else is free to investigate further in this area. :) I tried to just copy signtool.* and that didn't work, so at least some of the other files are needed.) diff --git a/branding/templates/windows/tools/windows10-signing.zip b/branding/templates/windows/tools/windows10-signing.zip new file mode 100644 index 0000000000000000000000000000000000000000..2d1858da91277b9cd3b7eac9a9c438c36f336e6e Binary files /dev/null and b/branding/templates/windows/tools/windows10-signing.zip differ diff --git a/pkg/config/config.go b/pkg/config/config.go index 682ce9c342a6cb30bef5095c37dc4db660462796..18f6667eb034233d713507689daaacd431902b03 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,6 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by vendorize.py -// At 2019-07-09 18:27:17 +// At 2019-07-09 18:54:26 package config