#!/usr/bin/env python3
#
# Copyright (C) 2022-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# 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 .
import argparse
from jinja2 import Template
from textwrap import fill
from vyos.config import Config
from vyos.ifconfig import Section
client_config = """
client
nobind
remote {{ local_host if local_host else 'x.x.x.x' }} {{ port }}
remote-cert-tls server
proto {{ 'tcp-client' if protocol == 'tcp-passive' else 'udp' }}
dev {{ device_type }}
dev-type {{ device_type }}
persist-key
persist-tun
verb 3
# Encryption options
{# Define the encryption map #}
{% set encryption_map = {
'des': 'DES-CBC',
'3des': 'DES-EDE3-CBC',
'bf128': 'BF-CBC',
'bf256': 'BF-CBC',
'aes128gcm': 'AES-128-GCM',
'aes128': 'AES-128-CBC',
'aes192gcm': 'AES-192-GCM',
'aes192': 'AES-192-CBC',
'aes256gcm': 'AES-256-GCM',
'aes256': 'AES-256-CBC'
} %}
{% if encryption is defined and encryption is not none %}
{% if encryption.data_ciphers is defined and encryption.data_ciphers is not none %}
cipher {% for algo in encryption.data_ciphers %}
{{ encryption_map[algo] if algo in encryption_map.keys() else algo }}{% if not loop.last %}:{% endif %}
{% endfor %}
data-ciphers {% for algo in encryption.data_ciphers %}
{{ encryption_map[algo] if algo in encryption_map.keys() else algo }}{% if not loop.last %}:{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% if hash is defined and hash is not none %}
auth {{ hash }}
{% endif %}
{{ 'comp-lzo' if use_lzo_compression is defined else '' }}
-----BEGIN CERTIFICATE-----
{{ ca }}
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
{{ cert }}
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
{{ key }}
-----END PRIVATE KEY-----
"""
config = Config()
base = ['interfaces', 'openvpn']
if not config.exists(base):
print('OpenVPN not configured')
exit(0)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
"-i",
"--interface",
type=str,
help='OpenVPN interface the client is connecting to',
required=True,
)
parser.add_argument(
"-a", "--ca", type=str, help='OpenVPN CA cerificate', required=True
)
parser.add_argument(
"-c", "--cert", type=str, help='OpenVPN client cerificate', required=True
)
parser.add_argument(
"-k", "--key", type=str, help='OpenVPN client cerificate key', action="store"
)
args = parser.parse_args()
interface = args.interface
ca = args.ca
cert = args.cert
key = args.key
if not key:
key = args.cert
if interface not in Section.interfaces('openvpn'):
exit(f'OpenVPN interface "{interface}" does not exist!')
if not config.exists(['pki', 'ca', ca, 'certificate']):
exit(f'OpenVPN CA certificate "{ca}" does not exist!')
if not config.exists(['pki', 'certificate', cert, 'certificate']):
exit(f'OpenVPN certificate "{cert}" does not exist!')
if not config.exists(['pki', 'certificate', cert, 'private', 'key']):
exit(f'OpenVPN certificate key "{key}" does not exist!')
config = config.get_config_dict(
base + [interface],
key_mangling=('-', '_'),
get_first_key=True,
with_recursive_defaults=True,
with_pki=True,
)
ca = config['pki']['ca'][ca]['certificate']
ca = fill(ca, width=64)
cert = config['pki']['certificate'][cert]['certificate']
cert = fill(cert, width=64)
key = config['pki']['certificate'][key]['private']['key']
key = fill(key, width=64)
config['ca'] = ca
config['cert'] = cert
config['key'] = key
config['port'] = '1194' if 'local_port' not in config else config['local_port']
client = Template(client_config, trim_blocks=True).render(config)
print(client)