|
| 1 | +#!/usr/bin/env python |
| 2 | +# SPDX-License-Identifier: LGPL-2.1-or-later |
| 3 | +# |
| 4 | +# Example to create a new VPN network connection profile. Currently supported only with tls-auth |
| 5 | +# |
| 6 | +# $ examples/async/add-openvpn-connection-async.py --help |
| 7 | +# usage: add-openvpn-connection.py [-h] [-c CONN_ID] [-d DEV] [--remote REMOTE] [--remote-cert-tls] [-a] [--save] |
| 8 | +# [--ca, CA_PATH] [--cert, CERT_PATH] [--key, KEY_path] [--ta, TA_PATH] |
| 9 | +# |
| 10 | +# Optional arguments have example values: |
| 11 | +# |
| 12 | +# optional arguments: |
| 13 | +# -h, --help show this help message and exit |
| 14 | +# -c CONN_ID Connection Id |
| 15 | +# -u UUID Connection UUID |
| 16 | +# -f OVPN .ovpn connection file |
| 17 | +# -a autoconnect |
| 18 | +# --save Save |
| 19 | +# --ca Path to CA file |
| 20 | +# --cert Path to cert file |
| 21 | +# --key Path to key file |
| 22 | +# --ta Path to tls-auth file |
| 23 | +# |
| 24 | +# $ add-vpn-connection.py |
| 25 | +# New unsaved connection profile created, show it with: |
| 26 | +# nmcli connection show "MyConnectionExample"|grep -v -e -- -e default |
| 27 | +# |
| 28 | +# Connection Profile settings are described at: |
| 29 | +# https://networkmanager.dev/docs/api/latest/ref-settings.html |
| 30 | +# |
| 31 | +# Note: By default, it uses add_connection_unsaved() to add a temporary |
| 32 | +# memory-only connection which is not saved to the system-connections folder: |
| 33 | +# For reference, see: https://networkmanager.dev/docs/api/latest/spec.html |
| 34 | +# -> org.freedesktop.NetworkManager.Settings (Settings Profile Manager) |
| 35 | + |
| 36 | +import asyncio |
| 37 | +import functools |
| 38 | +import logging |
| 39 | +import sdbus |
| 40 | +from uuid import uuid4 |
| 41 | +from argparse import ArgumentParser |
| 42 | +from pprint import pformat |
| 43 | +from sdbus_async.networkmanager import ( |
| 44 | + NetworkManagerSettings as SettingsManager, |
| 45 | + ConnectionType, |
| 46 | +) |
| 47 | +from sdbus_async.networkmanager.settings import ( |
| 48 | + ConnectionProfile, |
| 49 | + ConnectionSettings, |
| 50 | + Ipv4Settings, |
| 51 | + Ipv6Settings, |
| 52 | + VpnSettings |
| 53 | +) |
| 54 | + |
| 55 | + |
| 56 | +async def add_vpn_connection_async(conn_id: str, |
| 57 | + dev: str, |
| 58 | + remote: str, |
| 59 | + remote_cert_tls: str, |
| 60 | + uuid, |
| 61 | + auto: bool, |
| 62 | + save: bool, |
| 63 | + ca: str, |
| 64 | + cert: str, |
| 65 | + key: str, |
| 66 | + ta: str) -> str: |
| 67 | + # Add a temporary (not yet saved) network connection profile |
| 68 | + # param Namespace args: dev, remote, remote_cert_tls, ca_path, cert_path, key_path, ta_path |
| 69 | + # return: dbus connection path of the created connection profile |
| 70 | + |
| 71 | + info = logging.getLogger().info |
| 72 | + |
| 73 | + # If we add many connections passing the same id, things get messy. Check: |
| 74 | + if await SettingsManager().get_connections_by_id(conn_id): |
| 75 | + print(f'Connection "{conn_id}" exists, remove it first') |
| 76 | + print(f'Run: nmcli connection delete "{conn_id}"') |
| 77 | + return "" |
| 78 | + |
| 79 | + profile = ConnectionProfile( |
| 80 | + connection=ConnectionSettings( |
| 81 | + connection_id=conn_id, |
| 82 | + uuid=str(uuid), |
| 83 | + connection_type=ConnectionType.VPN.value, |
| 84 | + autoconnect=bool(auto), |
| 85 | + ), |
| 86 | + ipv4=Ipv4Settings(method="auto"), |
| 87 | + ipv6=Ipv6Settings(method="auto"), |
| 88 | + vpn=VpnSettings(data={ |
| 89 | + 'ca': ca, |
| 90 | + 'cert': cert, |
| 91 | + 'cert-pass-flags': '0', |
| 92 | + 'connection-type': 'tls', |
| 93 | + 'dev': dev, |
| 94 | + 'key': key, |
| 95 | + 'remote': remote, |
| 96 | + 'remote-cert-tls': remote_cert_tls, |
| 97 | + 'ta': ta, |
| 98 | + 'ta-dir': '1' |
| 99 | + }, service_type='org.freedesktop.NetworkManager.openvpn') |
| 100 | + ) |
| 101 | + |
| 102 | + s = SettingsManager() |
| 103 | + save = bool(save) |
| 104 | + addconnection = s.add_connection if save else s.add_connection_unsaved |
| 105 | + connection_settings_dbus_path = await addconnection(profile.to_dbus()) |
| 106 | + created = "created and saved" if save else "created" |
| 107 | + info(f"New unsaved connection profile {created}, show it with:") |
| 108 | + info(f'nmcli connection show "{conn_id}"|grep -v -e -- -e default') |
| 109 | + info("Settings used:") |
| 110 | + info(functools.partial(pformat, sort_dicts=False)(profile.to_settings_dict())) |
| 111 | + return connection_settings_dbus_path |
| 112 | + |
| 113 | + |
| 114 | +if __name__ == "__main__": |
| 115 | + logging.basicConfig(format="%(message)s", level=logging.INFO) |
| 116 | + p = ArgumentParser(description="Optional arguments have example values:") |
| 117 | + conn_id = "MyConnectionExample" |
| 118 | + p.add_argument("-c", dest="conn_id", default=conn_id, help="Connection Id") |
| 119 | + p.add_argument("-u", dest="uuid", default=uuid4(), help="Connection UUID") |
| 120 | + p.add_argument("--dev", dest="dev", default="tun", help="VPN Dev") |
| 121 | + p.add_argument("--remote", dest="remote", default="example.com:443:tcp", help="VPN Remote") |
| 122 | + p.add_argument("--remote-cert-tls", dest="remote_cert_tls", default="server", help="VPN Remote cert tls") |
| 123 | + p.add_argument("--ca", dest="ca", required=True, help="VPN CA file path") |
| 124 | + p.add_argument("--cert", dest="cert", required=True, help="VPN cert file path") |
| 125 | + p.add_argument("--key", dest="key", required=True, help="VPN key file path") |
| 126 | + p.add_argument("--ta", dest="ta", required=True, help="VPN TA file path") |
| 127 | + p.add_argument("-a", dest="auto", action="store_true", help="autoconnect") |
| 128 | + p.add_argument("--save", dest="save", action="store_true", help="Save") |
| 129 | + args = p.parse_args() |
| 130 | + sdbus.set_default_bus(sdbus.sd_bus_open_system()) |
| 131 | + if connection_dpath := asyncio.run(add_vpn_connection_async(**vars(args))): |
| 132 | + print(f"Path of the new connection: {connection_dpath}") |
| 133 | + print(f"UUID of the new connection: {args.uuid}") |
| 134 | + else: |
| 135 | + print("Error: No new connection created.") |
0 commit comments