Last active
May 31, 2024 09:02
-
-
Save brianbruggeman/f032f5b8e4b7fc1c63c8691071be5946 to your computer and use it in GitHub Desktop.
Convert Viscosity to Open VPN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Public Domain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
click |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
"""Converts viscosity export files into an open vpn package | |
Usage: viscosity-to-openvpn.py <input> <output> | |
""" | |
import io | |
import os | |
import sys | |
import tarfile | |
import click | |
if sys.version.startswith('3'): | |
unicode = str | |
# ---------------------------------------------------------------------- | |
# Exceptions | |
# ---------------------------------------------------------------------- | |
class ConversionError(Exception): | |
"""Base conversion error""" | |
pass | |
class NoConnectionName(ConversionError): | |
"""No connection name was available""" | |
pass | |
class NoCertificateData(ConversionError): | |
"""No certificate data was found within certificate file""" | |
pass | |
class NoCertificateFile(ConversionError): | |
"""File was not available within archive""" | |
pass | |
# ---------------------------------------------------------------------- | |
# Command-line Interface | |
# ---------------------------------------------------------------------- | |
@click.command() | |
@click.argument('input-path', type=click.Path(exists=True)) | |
@click.argument('output', required=False, type=click.Path(), default=None) | |
def convert(input_path, output=None): | |
'''Converts Viscosity package | |
Args: | |
input (str): path to folder or file input | |
output (str): path to folder output [default: None] | |
''' | |
if input_path.endswith('.visc'): | |
output = input_path if output is None else output | |
if output and not os.path.exists(output): | |
output = input_path | |
files = [os.path.join(input_path, filename) for filename in os.listdir(input_path)] | |
for config_fp in files: | |
new_config = [] | |
if config_fp.endswith('.conf'): | |
with io.open(config_fp, encoding='utf-8') as stream: | |
connection_name = extract(stream, new_config, input_path=input_path) | |
new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) | |
new_config = '\n'.join(new_config) + '\n' | |
output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) | |
with io.open(output_filepath, 'w', encoding='utf-8') as out: | |
out.write(unicode(new_config)) | |
print('Wrote: {}'.format(output_filepath)) | |
elif input_path.endswith('.visz'): | |
if output is None: | |
output = os.path.dirname(input_path) | |
data = {} | |
with tarfile.open(input_path) as zipped: | |
for filepath, fileinfo in zip(zipped.getnames(), zipped.getmembers()): | |
if not fileinfo.isfile(): | |
continue | |
filename = filepath.split(os.path.sep)[-1] | |
data[filename] = zipped.extractfile(filepath).read() | |
for key in data: | |
if not key.endswith('.conf') or key.startswith('.'): | |
continue | |
new_config = [] | |
lines = data[key].split('\n') | |
connection_name = extract(lines, new_config, file_data=data) | |
new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) | |
new_config = '\n'.join(new_config) + '\n' | |
output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) | |
with io.open(output_filepath, 'w', encoding='utf-8') as out: | |
out.write(unicode(new_config)) | |
print('Wrote: {}'.format(output_filepath)) | |
# ---------------------------------------------------------------------- | |
# CLI Support | |
# ---------------------------------------------------------------------- | |
def extract(data, new_config, input_path=None, file_data={}): | |
certificate_files = ['ca', 'cert', 'key', 'tls-auth'] | |
connection_name = '' | |
for line in data: | |
line = line.rstrip() | |
if not line.strip(): | |
continue | |
# This was an invalid configuration, for some reason | |
elif line == 'compress lzo': | |
continue | |
elif line.startswith('#'): | |
if line.startswith('#viscosity name'): | |
connection_name = line.split('#viscosity name ', 1)[-1] | |
connection_name = connection_name.strip() | |
continue | |
try: | |
key, value = line.split(' ', 1) | |
value = value.strip() | |
except ValueError: | |
key, value = line, '' | |
if key in certificate_files: | |
if key == 'tls-auth': | |
try: | |
value, direction = value.split(' ', 1) | |
new_config.append('key-direction {}'.format(direction)) | |
except ValueError: | |
pass | |
if input_path: | |
cert_filepath = os.path.join(input_path, value) | |
with io.open(cert_filepath, encoding='utf-8') as cf: | |
certificate = cf.read() | |
else: | |
if value not in file_data: | |
raise NoCertificateFile('Could not find certificate file in archive') | |
certificate = file_data.get(value) | |
if not certificate: | |
raise NoCertificateData('Could not find certificate data') | |
new_config.append('<%s>' % key) | |
new_config.append(certificate) | |
new_config.append('</%s>' % key) | |
continue | |
new_config.append(line) | |
if not connection_name.strip(): | |
raise NoConnectionName('Could not find connection name in file. Aborting') | |
return connection_name | |
if __name__ == '__main__': | |
convert() |
Thank you for this, it was super helpful!
i update the code, https://gist.github.com/vinicius795/e975688fa8ffcba549d8240ecf0a7f9f now it works in python 3.10
if __name__ == '__main__':
preconvert()
def preconvert(input_path, output=None):
if input_path.endswith('.visc') or input_path.endswith('.visz'):
convert(input_path,output)
return
for myfile in os.listdir(input_path):
if myfile.endswith('.visc') or myfile.endswith('.visz'):
convert(os.path.join(input_path, myfile))
this change give ability to convert all files in a folder : python viscosity-to-openvpn.py clients/
at the same time you still cat convert just single file: viscosity-to-openvpn.py clients/client1.visz
something still needs to be fixed:
python3 viscosity-to-openvpn.py ovpn.visz
Traceback (most recent call last):
File "viscosity-to-openvpn.py", line 165, in <module>
convert()
File "/usr/lib/python3/dist-packages/click/core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "/usr/lib/python3/dist-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/usr/lib/python3/dist-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/lib/python3/dist-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "viscosity-to-openvpn.py", line 81, in convert
data[filename] = zipped.extractfile(filepath).read().decode('utf8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xab in position 45: invalid start byte
This other python 2 script worked for me: https://gist.github.com/ishahid/693c2c97b3236a3c2416fc09ab170244
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You have to specify the file path to your viscosity file, eg:
c:_TEMP>python viscosity-to-openvpn.py c:_TEMP\Your_viscosity_profile.visc
in this example, i have a folder '_TEMP' on C:
inside this folder is the python script located, aswell as the *.visc profile