diff options
-rw-r--r-- | data/templates/openvpn/server.conf.tmpl | 2 | ||||
-rw-r--r-- | op-mode-definitions/generate-wireguard.xml.in | 26 | ||||
-rw-r--r-- | op-mode-definitions/pki.xml.in | 82 | ||||
-rw-r--r-- | python/vyos/configdict.py | 4 | ||||
-rw-r--r-- | python/vyos/ethtool.py | 19 | ||||
-rw-r--r-- | python/vyos/ifconfig/ethernet.py | 42 | ||||
-rw-r--r-- | python/vyos/util.py | 4 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-ethernet.py | 2 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-openvpn.py | 81 | ||||
-rwxr-xr-x | src/conf_mode/policy.py | 1 | ||||
-rw-r--r-- | src/etc/systemd/system/openvpn@.service.d/override.conf | 4 | ||||
-rwxr-xr-x | src/op_mode/pki.py | 142 |
12 files changed, 194 insertions, 215 deletions
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index 9b07a9ba2..9e4cc6813 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -7,8 +7,6 @@ # verb 3 -user {{ daemon_user }} -group {{ daemon_group }} dev-type {{ device_type }} dev {{ ifname }} persist-key diff --git a/op-mode-definitions/generate-wireguard.xml.in b/op-mode-definitions/generate-wireguard.xml.in index 6557b463b..259c9a898 100644 --- a/op-mode-definitions/generate-wireguard.xml.in +++ b/op-mode-definitions/generate-wireguard.xml.in @@ -4,9 +4,27 @@ <children> <node name="wireguard"> <properties> - <help>Generate Wireguard keys</help> + <help>Generate WireGuard keys</help> </properties> <children> + <leafNode name="default-keypair"> + <properties> + <help>generates the wireguard default-keypair</help> + </properties> + <command>echo "This command is deprecated. Please use: \"generate pki wireguard key-pair\""</command> + </leafNode> + <leafNode name="preshared-key"> + <properties> + <help>generate a wireguard preshared key</help> + </properties> + <command>echo "This command is deprecated. Please use: \"generate pki wireguard pre-shared-key\""</command> + </leafNode> + <tagNode name="named-keypairs"> + <properties> + <help>Generates named wireguard keypairs</help> + </properties> + <command>echo "This command is deprecated. Please use: \"generate pki wireguard key-pair install wgN\""</command> + </tagNode> <tagNode name="client-config"> <properties> <help>Generate Client config QR code</help> @@ -59,12 +77,6 @@ </tagNode> </children> </tagNode> - <leafNode name="key-pair"> - <properties> - <help>Generate Wireguard key pair for use with server or peer</help> - </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "noname"</command> - </leafNode> </children> </node> </children> diff --git a/op-mode-definitions/pki.xml.in b/op-mode-definitions/pki.xml.in index a11814c8a..6b9b0d3f6 100644 --- a/op-mode-definitions/pki.xml.in +++ b/op-mode-definitions/pki.xml.in @@ -282,60 +282,66 @@ </node> <node name="wireguard"> <properties> - <help>Generate Wireguard keys</help> + <help>Generate WireGuard keys</help> </properties> <children> <node name="key-pair"> <properties> - <help>Generate Wireguard key pair for use with server or peer</help> + <help>Generate WireGuard public/private key-pair</help> </properties> <children> - <tagNode name="file"> + <node name="install"> <properties> - <help>Write generated Wireguard keys into the specified filename</help> - <completionHelp> - <list><filename></list> - </completionHelp> + <help>Generate CLI commands to install WireGuard key to configuration</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "$6" --file</command> - </tagNode> - <tagNode name="install"> - <properties> - <help>Commands for installing generated Wireguard key into running configuration</help> - <completionHelp> - <list><interface> <peer></list> - </completionHelp> - </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "$6" --install</command> - </tagNode> + <children> + <tagNode name="interface"> + <properties> + <help>WireGuard Interface used in install command</help> + <completionHelp> + <path>interfaces wireguard</path> + </completionHelp> + </properties> + <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key --interface "$7" --install</command> + </tagNode> + </children> + </node> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key "noname"</command> + <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --key</command> </node> - <node name="pre-shared-key"> + <node name="preshared-key"> <properties> - <help>Generate pre-shared key for use with a Wireguard peer</help> + <help>Generate WireGuard pre-shared key</help> </properties> <children> - <tagNode name="file"> + <node name="install"> <properties> - <help>Write generated Wireguard PSK into the specified filename</help> - <completionHelp> - <list><filename></list> - </completionHelp> + <help>Generate CLI commands to install WireGuard key to configuration</help> </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "$6" --file</command> - </tagNode> - <tagNode name="install"> - <properties> - <help>Commands for installing generated Wireguard PSK on specified peer into running configuration</help> - <completionHelp> - <list><peer></list> - </completionHelp> - </properties> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "$6" --install</command> - </tagNode> + <children> + <tagNode name="interface"> + <properties> + <help>WireGuard Interface used in install command</help> + <completionHelp> + <path>interfaces wireguard</path> + </completionHelp> + </properties> + <children> + <tagNode name="peer"> + <properties> + <help>Interface used for install command</help> + <completionHelp> + <path>interfaces wireguard ${COMP_WORDS[COMP_CWORD-2]} peer</path> + </completionHelp> + </properties> + <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk --interface "$7" --peer "$9" --install</command> + </tagNode> + </children> + </tagNode> + </children> + </node> </children> - <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk "noname"</command> + <command>sudo ${vyos_op_scripts_dir}/pki.py --action generate --wireguard --psk</command> </node> </children> </node> diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index e15579b95..24b76fb0b 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -347,8 +347,8 @@ def get_interface_dict(config, base, ifname=''): # setup config level which is extracted in get_removed_vlans() config.set_level(base + [ifname]) - dict = config.get_config_dict([], key_mangling=('-', '_'), - get_first_key=True) + dict = config.get_config_dict([], key_mangling=('-', '_'), get_first_key=True, + no_tag_node_value_mangle=True) # Check if interface has been removed. We must use exists() as # get_config_dict() will always return {} - even when an empty interface diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py index 609d83b5e..bc95767b1 100644 --- a/python/vyos/ethtool.py +++ b/python/vyos/ethtool.py @@ -46,7 +46,7 @@ class Ethtool: _ring_buffers_max = { } _driver_name = None _auto_negotiation = None - _flow_control = None + _flow_control = False _flow_control_enabled = None def __init__(self, ifname): @@ -134,6 +134,12 @@ class Ethtool: # ['Autonegotiate:', 'on'] self._flow_control_enabled = out.splitlines()[1].split()[-1] + def get_auto_negotiation(self): + return self._auto_negotiation + + def get_driver_name(self): + return self._driver_name + def _get_generic(self, feature): """ Generic method to read self._features and return a tuple for feature @@ -184,12 +190,12 @@ class Ethtool: the underlaying network adapter. """ if isinstance(speed, int): speed = str(speed) - if not speed.isdigit(): + if speed != 'auto' and not speed.isdigit(): raise ValueError(f'Value "{speed}" for speed is invalid!') - if duplex not in ['full', 'half']: + if duplex not in ['auto', 'full', 'half']: raise ValueError(f'Value "{duplex}" for duplex is invalid!') - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False if speed in self._speed_duplex: @@ -199,7 +205,7 @@ class Ethtool: def check_flow_control(self): """ Check if the NIC supports flow-control """ - if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: + if self.get_driver_name() in ['vmxnet3', 'virtio_net', 'xen_netfront']: return False return self._flow_control @@ -208,6 +214,3 @@ class Ethtool: raise ValueError('Interface does not support changing '\ 'flow-control settings!') return self._flow_control_enabled - - def get_auto_negotiation(self): - return self._auto_negotiation diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py index 7bd269491..2e59a7afc 100644 --- a/python/vyos/ifconfig/ethernet.py +++ b/python/vyos/ifconfig/ethernet.py @@ -80,25 +80,6 @@ class EthernetIf(Interface): super().__init__(ifname, **kargs) self.ethtool = Ethtool(ifname) - def get_driver_name(self): - """ - Return the driver name used by NIC. Some NICs don't support all - features e.g. changing link-speed, duplex - - Example: - >>> from vyos.ifconfig import EthernetIf - >>> i = EthernetIf('eth0') - >>> i.get_driver_name() - 'vmxnet3' - """ - ifname = self.config['ifname'] - sysfs_file = f'/sys/class/net/{ifname}/device/driver/module' - if os.path.exists(sysfs_file): - link = os.readlink(sysfs_file) - return os.path.basename(link) - else: - return None - def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. @@ -115,11 +96,9 @@ class EthernetIf(Interface): if enable not in ['on', 'off']: raise ValueError("Value out of range") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'flow control settings!') - return + if not self.ethtool.check_flow_control(): + self._debug_msg(f'NIC driver does not support changing flow control settings!') + return False current = self.ethtool.get_flow_control() if current != enable: @@ -152,10 +131,8 @@ class EthernetIf(Interface): if duplex not in ['auto', 'full', 'half']: raise ValueError("Value out of range (duplex)") - driver_name = self.get_driver_name() - if driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']: - self._debug_msg(f'{driver_name} driver does not support changing '\ - 'speed/duplex settings!') + if not self.ethtool.check_speed_duplex(speed, duplex): + self._debug_msg(f'NIC driver does not support changing speed/duplex settings!') return # Get current speed and duplex settings: @@ -165,9 +142,12 @@ class EthernetIf(Interface): # bail out early as nothing is to change return else: - # read in current speed and duplex settings - cur_speed = read_file(f'/sys/class/net/{ifname}/speed') - cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex') + # XXX: read in current speed and duplex settings + # There are some "nice" NICs like AX88179 which do not support + # reading the speed thus we simply fallback to the supplied speed + # to not cause any change here and raise an exception. + cur_speed = read_file(f'/sys/class/net/{ifname}/speed', speed) + cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex', duplex) if (cur_speed == speed) and (cur_duplex == duplex): # bail out early as nothing is to change return diff --git a/python/vyos/util.py b/python/vyos/util.py index b41c5b346..849b27d3b 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -197,7 +197,7 @@ def read_file(fname, defaultonfailure=None): return defaultonfailure raise e -def write_file(fname, data, defaultonfailure=None, user=None, group=None): +def write_file(fname, data, defaultonfailure=None, user=None, group=None, mode=None): """ Write content of data to given fname, should defaultonfailure be not None, it is returned on failure to read. @@ -215,6 +215,7 @@ def write_file(fname, data, defaultonfailure=None, user=None, group=None): with open(fname, 'w') as f: bytes = f.write(data) chown(fname, user, group) + chmod(fname, mode) return bytes except Exception as e: if defaultonfailure is not None: @@ -295,7 +296,6 @@ def makedir(path, user=None, group=None): os.makedirs(path, mode=0o755) chown(path, user, group) - def colon_separated_to_dict(data_string, uniquekeys=False): """ Converts a string containing newline-separated entries of colon-separated key-value pairs into a dict. diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py index 21a04f954..e7250fb49 100755 --- a/src/conf_mode/interfaces-ethernet.py +++ b/src/conf_mode/interfaces-ethernet.py @@ -126,7 +126,7 @@ def verify(ethernet): if not os.path.exists(f'/sys/class/net/{ifname}/queues/rx-0/rps_cpus'): raise ConfigError('Interface does not suport RPS!') - driver = EthernetIf(ifname).get_driver_name() + driver = ethtool.get_driver_name() # T3342 - Xen driver requires special treatment if driver == 'vif': if int(ethernet['mtu']) > 1500 and dict_search('offload.sg', ethernet) == None: diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 6be4e918b..02b7f83bf 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -45,9 +45,10 @@ from vyos.template import is_ipv4 from vyos.template import is_ipv6 from vyos.util import call from vyos.util import chown -from vyos.util import chmod_600 from vyos.util import dict_search from vyos.util import dict_search_args +from vyos.util import makedir +from vyos.util import write_file from vyos.validate import is_addr_assigned from vyos import ConfigError @@ -80,9 +81,6 @@ def get_config(config=None): openvpn['pki'] = tmp_pki openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn) - openvpn['daemon_user'] = user - openvpn['daemon_group'] = group - return openvpn def is_ec_private_key(pki, cert_name): @@ -128,7 +126,7 @@ def verify_pki(openvpn): if tls['ca_certificate'] not in pki['ca']: raise ConfigError(f'Invalid CA certificate on openvpn interface {interface}') - if not (mode == 'client' and 'auth_key' in tls): + if mode != 'client' and 'auth_key' not in tls: if 'certificate' not in tls: raise ConfigError(f'Missing "tls certificate" on openvpn interface {interface}') @@ -449,7 +447,6 @@ def verify(openvpn): def generate_pki_files(openvpn): pki = openvpn['pki'] - if not pki: return None @@ -457,16 +454,11 @@ def generate_pki_files(openvpn): shared_secret_key = dict_search_args(openvpn, 'shared_secret_key') tls = dict_search_args(openvpn, 'tls') - files = [] - if shared_secret_key: pki_key = pki['openvpn']['shared_secret'][shared_secret_key] key_path = os.path.join(cfg_dir, f'{interface}_shared.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group) if tls: if 'ca_certificate' in tls: @@ -475,20 +467,15 @@ def generate_pki_files(openvpn): if 'certificate' in pki_ca: cert_path = os.path.join(cfg_dir, f'{interface}_ca.pem') - - with open(cert_path, 'w') as f: - f.write(wrap_certificate(pki_ca['certificate'])) - - files.append(cert_path) + write_file(cert_path, wrap_certificate(pki_ca['certificate']), + user=user, group=group, mode=0o600) if 'crl' in pki_ca: for crl in pki_ca['crl']: crl_path = os.path.join(cfg_dir, f'{interface}_crl.pem') + write_file(crl_path, wrap_crl(crl), user=user, group=group, + mode=0o600) - with open(crl_path, 'w') as f: - f.write(wrap_crl(crl)) - - files.append(crl_path) openvpn['tls']['crl'] = True if 'certificate' in tls: @@ -497,19 +484,14 @@ def generate_pki_files(openvpn): if 'certificate' in pki_cert: cert_path = os.path.join(cfg_dir, f'{interface}_cert.pem') - - with open(cert_path, 'w') as f: - f.write(wrap_certificate(pki_cert['certificate'])) - - files.append(cert_path) + write_file(cert_path, wrap_certificate(pki_cert['certificate']), + user=user, group=group, mode=0o600) if 'private' in pki_cert and 'key' in pki_cert['private']: key_path = os.path.join(cfg_dir, f'{interface}_cert.key') + write_file(key_path, wrap_private_key(pki_cert['private']['key']), + user=user, group=group, mode=0o600) - with open(key_path, 'w') as f: - f.write(wrap_private_key(pki_cert['private']['key'])) - - files.append(key_path) openvpn['tls']['private_key'] = True if 'dh_params' in tls: @@ -518,11 +500,8 @@ def generate_pki_files(openvpn): if 'parameters' in pki_dh: dh_path = os.path.join(cfg_dir, f'{interface}_dh.pem') - - with open(dh_path, 'w') as f: - f.write(wrap_dh_parameters(pki_dh['parameters'])) - - files.append(dh_path) + write_file(dh_path, wrap_dh_parameters(pki_dh['parameters']), + user=user, group=group, mode=0o600) if 'auth_key' in tls: key_name = tls['auth_key'] @@ -530,11 +509,8 @@ def generate_pki_files(openvpn): if 'key' in pki_key: key_path = os.path.join(cfg_dir, f'{interface}_auth.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group, mode=0o600) if 'crypt_key' in tls: key_name = tls['crypt_key'] @@ -542,18 +518,17 @@ def generate_pki_files(openvpn): if 'key' in pki_key: key_path = os.path.join(cfg_dir, f'{interface}_crypt.key') - - with open(key_path, 'w') as f: - f.write(wrap_openvpn_key(pki_key['key'])) - - files.append(key_path) - - return files + write_file(key_path, wrap_openvpn_key(pki_key['key']), + user=user, group=group, mode=0o600) def generate(openvpn): interface = openvpn['ifname'] directory = os.path.dirname(cfg_file.format(**openvpn)) + # create base config directory on demand + makedir(directory, user, group) + # enforce proper permissions on /run/openvpn + chown(directory, user, group) # we can't know in advance which clients have been removed, # thus all client configs will be removed and re-added on demand @@ -565,12 +540,10 @@ def generate(openvpn): return None # create client config directory on demand - if not os.path.exists(ccd_dir): - os.makedirs(ccd_dir, 0o755) - chown(ccd_dir, user, group) + makedir(ccd_dir, user, group) # Fix file permissons for keys - fix_permissions = generate_pki_files(openvpn) + generate_pki_files(openvpn) # Generate User/Password authentication file if 'authentication' in openvpn: @@ -598,10 +571,6 @@ def generate(openvpn): render(cfg_file.format(**openvpn), 'openvpn/server.conf.tmpl', openvpn, formater=lambda _: _.replace(""", '"'), user=user, group=group) - # Fixup file permissions - for file in fix_permissions: - chmod_600(file) - return None def apply(openvpn): diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py index d56bae9e9..1a03d520b 100755 --- a/src/conf_mode/policy.py +++ b/src/conf_mode/policy.py @@ -190,6 +190,7 @@ def apply(policy): frr_cfg.modify_section(r'^bgp community-list .*') frr_cfg.modify_section(r'^bgp extcommunity-list .*') frr_cfg.modify_section(r'^bgp large-community-list .*') + frr_cfg.modify_section(r'^route-map .*') frr_cfg.add_before('^line vty', policy['new_frr_config']) frr_cfg.commit_configuration(bgp_daemon) diff --git a/src/etc/systemd/system/openvpn@.service.d/override.conf b/src/etc/systemd/system/openvpn@.service.d/override.conf index 7946484a3..03fe6b587 100644 --- a/src/etc/systemd/system/openvpn@.service.d/override.conf +++ b/src/etc/systemd/system/openvpn@.service.d/override.conf @@ -7,3 +7,7 @@ WorkingDirectory= WorkingDirectory=/run/openvpn ExecStart= ExecStart=/usr/sbin/openvpn --daemon openvpn-%i --config %i.conf --status %i.status 30 --writepid %i.pid +User=openvpn +Group=openvpn +AmbientCapabilities=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE +CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 36891d080..d28cee5d0 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -17,7 +17,6 @@ import argparse import ipaddress import os -import re import sys import tabulate @@ -44,12 +43,14 @@ auth_dir = '/config/auth' conf = ConfigTreeQuery() def get_default_values(): # Fetch default x509 values - base = ['pki', 'x509', 'default'] x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) default_values = defaults(base) - return dict_merge(default_values, x509_defaults) + x509_defaults = dict_merge(default_values, x509_defaults) + + return x509_defaults def get_config_ca_certificate(name=None): # Fetch ca certificates from config @@ -63,7 +64,8 @@ def get_config_ca_certificate(name=None): return False return conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) def get_config_certificate(name=None): # Get certificates from config @@ -77,7 +79,8 @@ def get_config_certificate(name=None): return False return conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) def get_certificate_ca(cert, ca_certs): # Find CA certificate for given certificate @@ -103,12 +106,14 @@ def get_config_revoked_certificates(): if conf.exists(ca_base): ca_certificates = conf.get_config_dict(ca_base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) certs.extend(ca_certificates.values()) if conf.exists(cert_base): certificates = conf.get_config_dict(cert_base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) + get_first_key=True, + no_tag_node_value_mangle=True) certs.extend(certificates.values()) return [cert_dict for cert_dict in certs if 'revoke' in cert_dict] @@ -139,39 +144,41 @@ def get_revoked_by_serial_numbers(serial_numbers=[]): def install_certificate(name, cert='', private_key=None, key_type=None, key_passphrase=None, is_ca=False): # Show conf commands for installing certificate prefix = 'ca' if is_ca else 'certificate' - print("Configure mode commands to install:") + print('Configure mode commands to install:') + base = f"set pki {prefix} {name}" if cert: cert_pem = "".join(encode_certificate(cert).strip().split("\n")[1:-1]) - print("set pki %s %s certificate '%s'" % (prefix, name, cert_pem)) + print(f"{base} certificate '{cert_pem}'") if private_key: key_pem = "".join(encode_private_key(private_key, passphrase=key_passphrase).strip().split("\n")[1:-1]) - print("set pki %s %s private key '%s'" % (prefix, name, key_pem)) + print(f"{base} private key '{key_pem}'") if key_passphrase: - print("set pki %s %s private password-protected" % (prefix, name)) + print(f"{base} private password-protected") def install_crl(ca_name, crl): # Show conf commands for installing crl print("Configure mode commands to install CRL:") crl_pem = "".join(encode_certificate(crl).strip().split("\n")[1:-1]) - print("set pki ca %s crl '%s'" % (ca_name, crl_pem)) + print(f"set pki ca {ca_name} crl '{crl_pem}'") def install_dh_parameters(name, params): # Show conf commands for installing dh params print("Configure mode commands to install DH parameters:") dh_pem = "".join(encode_dh_parameters(params).strip().split("\n")[1:-1]) - print("set pki dh %s parameters '%s'" % (name, dh_pem)) + print(f"set pki dh {name} parameters '{dh_pem}'") def install_ssh_key(name, public_key, private_key, passphrase=None): # Show conf commands for installing ssh key key_openssh = encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH') username = os.getlogin() type_key_split = key_openssh.split(" ") + + base = f"set system login user {username} authentication public-keys {name}" print("Configure mode commands to install SSH key:") - print("set system login user %s authentication public-keys %s key '%s'" % (username, name, type_key_split[1])) - print("set system login user %s authentication public-keys %s type '%s'" % (username, name, type_key_split[0])) - print("") + print(f"{base} key '{type_key_split[1]}'") + print(f"{base} type '{type_key_split[0]}'", end="\n\n") print(encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase)) def install_keypair(name, key_type, private_key=None, public_key=None, passphrase=None): @@ -184,7 +191,7 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras if install_public_key: install_public_pem = "".join(public_key_pem.strip().split("\n")[1:-1]) - print("set pki key-pair %s public key '%s'" % (name, install_public_pem)) + print(f"set pki key-pair {name} public key '{install_public_pem}'") else: print("Public key:") print(public_key_pem) @@ -195,30 +202,31 @@ def install_keypair(name, key_type, private_key=None, public_key=None, passphras if install_private_key: install_private_pem = "".join(private_key_pem.strip().split("\n")[1:-1]) - print("set pki key-pair %s private key '%s'" % (name, install_private_pem)) + print(f"set pki key-pair {name} private key '{install_private_pem}'") if passphrase: - print("set pki key-pair %s private password-protected" % (name,)) + print(f"set pki key-pair {name} private password-protected") else: print("Private key:") print(private_key_pem) -def install_wireguard_key(name, private_key, public_key): +def install_wireguard_key(interface, private_key, public_key): # Show conf commands for installing wireguard key pairs - is_interface = re.match(r'^wg[\d]+$', name) - - print("Configure mode commands to install key:") - if is_interface: - print("set interfaces wireguard %s private-key '%s'" % (name, private_key)) - print("") - print("Public key for use on peer configuration: " + public_key) - else: - print("set interfaces wireguard [INTERFACE] peer %s public-key '%s'" % (name, public_key)) - print("") - print("Private key for use on peer configuration: " + private_key) - -def install_wireguard_psk(name, psk): + from vyos.ifconfig import Section + if Section.section(interface) != 'wireguard': + print(f'"{interface}" is not a WireGuard interface name!') + exit(1) + + print("Configure mode commands to install key:", end="\n\n") + print(f"set interfaces wireguard {interface} private-key '{private_key}'", end="\n\n") + print(f"Public key to use on peer system: '{public_key}'") + +def install_wireguard_psk(interface, peer, psk): + from vyos.ifconfig import Section + if Section.section(interface) != 'wireguard': + print(f'"{interface}" is not a WireGuard interface name!') + exit(1) # Show conf commands for installing wireguard psk - print("set interfaces wireguard [INTERFACE] peer %s preshared-key '%s'" % (name, psk)) + print(f"set interfaces wireguard {interface} peer {peer} preshared-key '{psk}'") def ask_passphrase(): passphrase = None @@ -625,49 +633,37 @@ def generate_openvpn_key(name, install=False, file=False): key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings key_version = '1' + import re version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) if version_search: key_version = version_search[1] + base = f"set pki openvpn shared-secret {name}" print("Configure mode commands to install OpenVPN key:") - print("set pki openvpn shared-secret %s key '%s'" % (name, key_data)) - print("set pki openvpn shared-secret %s version '%s'" % (name, key_version)) + print(f"{base} key '{key_data}'") + print(f"{base} version '{key_version}'") if file: write_file(f'{name}.key', result) -def generate_wireguard_key(name, install=False, file=False): +def generate_wireguard_key(interface=None, install=False): private_key = cmd('wg genkey') public_key = cmd('wg pubkey', input=private_key) - if not install: - print("Private key: " + private_key) - print("Public key: " + public_key) - return None - - if install: - install_wireguard_key(name, private_key, public_key) - - if file: - write_file(f'{name}_public.key', public_key) - write_file(f'{name}_private.key', private_key) + if interface and install: + install_wireguard_key(interface, private_key, public_key) + else: + print(f'Private key: {private_key}') + print(f'Public key: {public_key}', end='\n\n') -def generate_wireguard_psk(name, install=False, file=False): +def generate_wireguard_psk(interface=None, peer=None, install=False): psk = cmd('wg genpsk') - - if not install and not file: - print("Pre-shared key:") - print(psk) - return None - - if install: - install_wireguard_psk(name, psk) - - if file: - write_file(f'{name}.key', psk) + if interface and peer and install: + install_wireguard_psk(interface, peer, psk) + else: + print(f'Pre-shared key: {psk}') # Show functions - def show_certificate_authority(name=None): headers = ['Name', 'Subject', 'Issuer CN', 'Issued', 'Expiry', 'Private Key', 'Parent'] data = [] @@ -784,10 +780,13 @@ if __name__ == '__main__': # OpenVPN parser.add_argument('--openvpn', help='OpenVPN TLS key', required=False) - # Wireguard + # WireGuard parser.add_argument('--wireguard', help='Wireguard', action='store_true') - parser.add_argument('--key', help='Wireguard key pair', required=False) - parser.add_argument('--psk', help='Wireguard pre shared key', required=False) + group = parser.add_mutually_exclusive_group() + group.add_argument('--key', help='Wireguard key pair', action='store_true', required=False) + group.add_argument('--psk', help='Wireguard pre shared key', action='store_true', required=False) + parser.add_argument('--interface', help='Install generated keys into running-config for named interface', action='store') + parser.add_argument('--peer', help='Install generated keys into running-config for peer', action='store') # Global parser.add_argument('--file', help='Write generated keys into specified filename', action='store_true') @@ -809,21 +808,28 @@ if __name__ == '__main__': generate_certificate_selfsign(args.certificate, install=args.install, file=args.file) else: generate_certificate_request(name=args.certificate, install=args.install, file=args.file) + elif args.crl: generate_certificate_revocation_list(args.crl, install=args.install, file=args.file) + elif args.ssh: generate_ssh_keypair(args.ssh, install=args.install, file=args.file) + elif args.dh: generate_dh_parameters(args.dh, install=args.install, file=args.file) + elif args.keypair: generate_keypair(args.keypair, install=args.install, file=args.file) + elif args.openvpn: generate_openvpn_key(args.openvpn, install=args.install, file=args.file) + elif args.wireguard: if args.key: - generate_wireguard_key(args.key, install=args.install, file=args.file) - elif args.psk: - generate_wireguard_psk(args.psk, install=args.install, file=args.file) + generate_wireguard_key(args.interface, install=args.install) + if args.psk: + generate_wireguard_psk(args.interface, peer=args.peer, install=args.install) + elif args.action == 'show': if args.ca: show_certificate_authority(None if args.ca == 'all' else args.ca) |