diff options
Diffstat (limited to 'src/op_mode')
-rwxr-xr-x | src/op_mode/dns_forwarding_statistics.py | 2 | ||||
-rwxr-xr-x | src/op_mode/ikev2_profile_generator.py | 230 | ||||
-rwxr-xr-x | src/op_mode/ping.py | 4 | ||||
-rwxr-xr-x | src/op_mode/pki.py | 189 | ||||
-rwxr-xr-x | src/op_mode/show_dhcp.py | 7 | ||||
-rwxr-xr-x | src/op_mode/show_dhcpv6.py | 6 | ||||
-rwxr-xr-x | src/op_mode/show_vrf.py | 7 | ||||
-rwxr-xr-x | src/op_mode/wireguard.py | 159 | ||||
-rwxr-xr-x | src/op_mode/wireguard_client.py | 2 |
9 files changed, 376 insertions, 230 deletions
diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py index 1fb61d263..d79b6c024 100755 --- a/src/op_mode/dns_forwarding_statistics.py +++ b/src/op_mode/dns_forwarding_statistics.py @@ -11,7 +11,7 @@ PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' OUT_TMPL_SRC = """ DNS forwarding statistics: -Cache entries: {{ cache_entries -}} +Cache entries: {{ cache_entries }} Cache size: {{ cache_size }} kbytes """ diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py new file mode 100755 index 000000000..d45525431 --- /dev/null +++ b/src/op_mode/ikev2_profile_generator.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 <http://www.gnu.org/licenses/>. + +import argparse + +from jinja2 import Template +from sys import exit +from socket import getfqdn +from cryptography.x509.oid import NameOID + +from vyos.config import Config +from vyos.pki import load_certificate +from vyos.template import render_to_string +from vyos.util import ask_input + +# Apple profiles only support one IKE/ESP encryption cipher and hash, whereas +# VyOS comes with a multitude of different proposals for a connection. +# +# We take all available proposals from the VyOS CLI and ask the user which one +# he would like to get enabled in his profile - thus there is limited possibility +# to select a proposal that is not supported on the connection profile. +# +# IOS supports IKE-SA encryption algorithms: +# - DES +# - 3DES +# - AES-128 +# - AES-256 +# - AES-128-GCM +# - AES-256-GCM +# - ChaCha20Poly1305 +# +vyos2apple_cipher = { + '3des' : '3DES', + 'aes128' : 'AES-128', + 'aes256' : 'AES-256', + 'aes128gcm128' : 'AES-128-GCM', + 'aes256gcm128' : 'AES-256-GCM', + 'chacha20poly1305' : 'ChaCha20Poly1305', +} + +# Windows supports IKE-SA encryption algorithms: +# - DES3 +# - AES128 +# - AES192 +# - AES256 +# - GCMAES128 +# - GCMAES192 +# - GCMAES256 +# +vyos2windows_cipher = { + '3des' : 'DES3', + 'aes128' : 'AES128', + 'aes192' : 'AES192', + 'aes256' : 'AES256', + 'aes128gcm128' : 'GCMAES128', + 'aes192gcm128' : 'GCMAES192', + 'aes256gcm128' : 'GCMAES256', +} + +# IOS supports IKE-SA integrity algorithms: +# - SHA1-96 +# - SHA1-160 +# - SHA2-256 +# - SHA2-384 +# - SHA2-512 +# +vyos2apple_integrity = { + 'sha1' : 'SHA1-96', + 'sha1_160' : 'SHA1-160', + 'sha256' : 'SHA2-256', + 'sha384' : 'SHA2-384', + 'sha512' : 'SHA2-512', +} + +# Windows supports IKE-SA integrity algorithms: +# - SHA1-96 +# - SHA1-160 +# - SHA2-256 +# - SHA2-384 +# - SHA2-512 +# +vyos2windows_integrity = { + 'sha1' : 'SHA196', + 'sha256' : 'SHA256', + 'aes128gmac' : 'GCMAES128', + 'aes192gmac' : 'GCMAES192', + 'aes256gmac' : 'GCMAES256', +} + +# IOS 14.2 and later do no support dh-group 1,2 and 5. Supported DH groups would +# be: 14, 15, 16, 17, 18, 19, 20, 21, 31 +ios_supported_dh_groups = ['14', '15', '16', '17', '18', '19', '20', '21', '31'] +# Windows 10 only allows a limited set of DH groups +windows_supported_dh_groups = ['1', '2', '14', '24'] + +parser = argparse.ArgumentParser() +parser.add_argument('--os', const='all', nargs='?', choices=['ios', 'windows'], help='Operating system used for config generation', required=True) +parser.add_argument("--connection", action="store", help='IPsec IKEv2 remote-access connection name from CLI', required=True) +parser.add_argument("--remote", action="store", help='VPN connection remote-address where the client will connect to', required=True) +parser.add_argument("--profile", action="store", help='IKEv2 profile name used in the profile list on the device') +parser.add_argument("--name", action="store", help='VPN connection name as seen in the VPN application later') +args = parser.parse_args() + +ipsec_base = ['vpn', 'ipsec'] +config_base = ipsec_base + ['remote-access', 'connection'] +pki_base = ['pki'] +conf = Config() +if not conf.exists(config_base): + exit('IPSec remote-access is not configured!') + +profile_name = 'VyOS IKEv2 Profile' +if args.profile: + profile_name = args.profile + +vpn_name = 'VyOS IKEv2 VPN' +if args.name: + vpn_name = args.name + +conn_base = config_base + [args.connection] +if not conf.exists(conn_base): + exit(f'IPSec remote-access connection "{args.connection}" does not exist!') + +data = conf.get_config_dict(conn_base, key_mangling=('-', '_'), + get_first_key=True, no_tag_node_value_mangle=True) + +data['profile_name'] = profile_name +data['vpn_name'] = vpn_name +data['remote'] = args.remote +# This is a reverse-DNS style unique identifier used to detect duplicate profiles +tmp = getfqdn().split('.') +tmp = reversed(tmp) +data['rfqdn'] = '.'.join(tmp) + +pki = conf.get_config_dict(pki_base, get_first_key=True) +ca_name = data['authentication']['x509']['ca_certificate'] +cert_name = data['authentication']['x509']['certificate'] + +ca_cert = load_certificate(pki['ca'][ca_name]['certificate']) +cert = load_certificate(pki['certificate'][cert_name]['certificate']) + +data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value +data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value +data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate']) + +esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'], + key_mangling=('-', '_'), get_first_key=True) +ike_proposal = conf.get_config_dict(ipsec_base + ['ike-group', data['ike_group'], 'proposal'], + key_mangling=('-', '_'), get_first_key=True) + + +# This script works only for Apple iOS/iPadOS and Windows. Both operating systems +# have different limitations thus we load the limitations based on the operating +# system used. + +vyos2client_cipher = vyos2apple_cipher if args.os == 'ios' else vyos2windows_cipher; +vyos2client_integrity = vyos2apple_integrity if args.os == 'ios' else vyos2windows_integrity; +supported_dh_groups = ios_supported_dh_groups if args.os == 'ios' else windows_supported_dh_groups; + +# Create a dictionary containing client conform IKE settings +ike = {} +count = 1 +for _, proposal in ike_proposal.items(): + if {'dh_group', 'encryption', 'hash'} <= set(proposal): + if (proposal['encryption'] in set(vyos2client_cipher) and + proposal['hash'] in set(vyos2client_integrity) and + proposal['dh_group'] in set(supported_dh_groups)): + + # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme + proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ] + proposal['hash'] = vyos2client_integrity[ proposal['hash'] ] + + ike.update( { str(count) : proposal } ) + count += 1 + +# Create a dictionary containing Apple conform ESP settings +esp = {} +count = 1 +for _, proposal in esp_proposals.items(): + if {'encryption', 'hash'} <= set(proposal): + if proposal['encryption'] in set(vyos2client_cipher) and proposal['hash'] in set(vyos2client_integrity): + # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme + proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ] + proposal['hash'] = vyos2client_integrity[ proposal['hash'] ] + + esp.update( { str(count) : proposal } ) + count += 1 +try: + if len(ike) > 1: + # Propare the input questions for the user + tmp = '\n' + for number, options in ike.items(): + tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}, DH group {options["dh_group"]}\n' + tmp += '\nSelect one of the above IKE groups: ' + data['ike_encryption'] = ike[ ask_input(tmp, valid_responses=list(ike)) ] + else: + data['ike_encryption'] = ike['1'] + + if len(esp) > 1: + tmp = '\n' + for number, options in esp.items(): + tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}\n' + tmp += '\nSelect one of the above ESP groups: ' + data['esp_encryption'] = esp[ ask_input(tmp, valid_responses=list(esp)) ] + else: + data['esp_encryption'] = esp['1'] + +except KeyboardInterrupt: + exit("Interrupted") + +print('\n\n==== <snip> ====') +if args.os == 'ios': + print(render_to_string('ipsec/ios_profile.tmpl', data)) + print('==== </snip> ====\n') + print('Save the XML from above to a new file named "vyos.mobileconfig" and E-Mail it to your phone.') +elif args.os == 'windows': + print(render_to_string('ipsec/windows_profile.tmpl', data)) + print('==== </snip> ====\n') diff --git a/src/op_mode/ping.py b/src/op_mode/ping.py index 924a889db..2144ab53c 100755 --- a/src/op_mode/ping.py +++ b/src/op_mode/ping.py @@ -51,7 +51,7 @@ options = { 'help': 'Number of seconds before ping exits' }, 'do-not-fragment': { - 'ping': '{command} -M dont', + 'ping': '{command} -M do', 'type': 'noarg', 'help': 'Set DF-bit flag to 1 for no fragmentation' }, @@ -220,6 +220,8 @@ if __name__ == '__main__': try: ip = socket.gethostbyname(host) + except UnicodeError: + sys.exit(f'ping: Unknown host: {host}') except socket.gaierror: ip = host diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 7dbeb4097..297270cf1 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -38,6 +38,8 @@ from vyos.util import cmd CERT_REQ_END = '-----END CERTIFICATE REQUEST-----' +auth_dir = '/config/auth' + # Helper Functions def get_default_values(): @@ -215,7 +217,7 @@ def install_wireguard_key(name, private_key, public_key): print("") print("Public key for use on peer configuration: " + public_key) else: - print("set interfaces wireguard [INTERFACE] peer %s pubkey '%s'" % (name, public_key)) + print("set interfaces wireguard [INTERFACE] peer %s public-key '%s'" % (name, public_key)) print("") print("Private key for use on peer configuration: " + private_key) @@ -230,6 +232,22 @@ def ask_passphrase(): passphrase = ask_input('Enter passphrase:') return passphrase +def write_file(filename, contents): + full_path = os.path.join(auth_dir, filename) + directory = os.path.dirname(full_path) + + if not os.path.exists(directory): + print('Failed to write file: directory does not exist') + return False + + if os.path.exists(full_path) and not ask_yes_no('Do you want to overwrite the existing file?'): + return False + + with open(full_path, 'w') as f: + f.write(contents) + + print(f'File written to {full_path}') + # Generation functions def generate_private_key(): @@ -266,7 +284,7 @@ def parse_san_string(san_string): output.append(value) return output -def generate_certificate_request(private_key=None, key_type=None, return_request=False, name=None, install=False, ask_san=True): +def generate_certificate_request(private_key=None, key_type=None, return_request=False, name=None, install=False, file=False, ask_san=True): if not private_key: private_key, key_type = generate_private_key() @@ -291,14 +309,19 @@ def generate_certificate_request(private_key=None, key_type=None, return_request passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_certificate(cert_req)) print(encode_private_key(private_key, passphrase=passphrase)) return None - print("Certificate request:") - print(encode_certificate(cert_req) + "\n") - install_certificate(name, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + if install: + print("Certificate request:") + print(encode_certificate(cert_req) + "\n") + install_certificate(name, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + + if file: + write_file(f'{name}.csr', encode_certificate(cert_req)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) def generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False, is_sub_ca=False): valid_days = ask_input('Enter how many days certificate will be valid:', default='365' if not is_ca else '1825', numeric_only=True) @@ -307,20 +330,25 @@ def generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False, is_sub_ cert_type = ask_input('Enter certificate type: (client, server)', default='server', valid_responses=['client', 'server']) return create_certificate(cert_req, ca_cert, ca_private_key, valid_days, cert_type, is_ca, is_sub_ca) -def generate_ca_certificate(name, install=False): +def generate_ca_certificate(name, install=False, file=False): private_key, key_type = generate_private_key() cert_req = generate_certificate_request(private_key, key_type, return_request=True, ask_san=False) cert = generate_certificate(cert_req, cert_req, private_key, is_ca=True) passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_certificate(cert)) print(encode_private_key(private_key, passphrase=passphrase)) return None - install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + if install: + install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + + if file: + write_file(f'{name}.pem', encode_certificate(cert)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_ca_certificate_sign(name, ca_name, install=False): +def generate_ca_certificate_sign(name, ca_name, install=False, file=False): ca_dict = get_config_ca_certificate(ca_name) if not ca_dict: @@ -374,14 +402,19 @@ def generate_ca_certificate_sign(name, ca_name, install=False): cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=True, is_sub_ca=True) passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_certificate(cert)) print(encode_private_key(private_key, passphrase=passphrase)) return None - install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + if install: + install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=True) + + if file: + write_file(f'{name}.pem', encode_certificate(cert)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_sign(name, ca_name, install=False): +def generate_certificate_sign(name, ca_name, install=False, file=False): ca_dict = get_config_ca_certificate(ca_name) if not ca_dict: @@ -435,27 +468,37 @@ def generate_certificate_sign(name, ca_name, install=False): cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False) passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_certificate(cert)) print(encode_private_key(private_key, passphrase=passphrase)) return None - install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=False) + if install: + install_certificate(name, cert, private_key, key_type, key_passphrase=passphrase, is_ca=False) + + if file: + write_file(f'{name}.pem', encode_certificate(cert)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_selfsign(name, install=False): +def generate_certificate_selfsign(name, install=False, file=False): private_key, key_type = generate_private_key() cert_req = generate_certificate_request(private_key, key_type, return_request=True) cert = generate_certificate(cert_req, cert_req, private_key, is_ca=False) passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_certificate(cert)) print(encode_private_key(private_key, passphrase=passphrase)) return None - install_certificate(name, cert, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + if install: + install_certificate(name, cert, private_key=private_key, key_type=key_type, key_passphrase=passphrase, is_ca=False) + + if file: + write_file(f'{name}.pem', encode_certificate(cert)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_certificate_revocation_list(ca_name, install=False): +def generate_certificate_revocation_list(ca_name, install=False, file=False): ca_dict = get_config_ca_certificate(ca_name) if not ca_dict: @@ -505,26 +548,35 @@ def generate_certificate_revocation_list(ca_name, install=False): print("Failed to create CRL") return None - if not install: + if not install and not file: print(encode_certificate(crl)) return None - install_crl(ca_name, crl) + if install: + install_crl(ca_name, crl) + + if file: + write_file(f'{name}.crl', encode_certificate(crl)) -def generate_ssh_keypair(name, install=False): +def generate_ssh_keypair(name, install=False, file=False): private_key, key_type = generate_private_key() public_key = private_key.public_key() passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH')) print("") print(encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase)) return None - install_ssh_key(name, public_key, private_key, passphrase) + if install: + install_ssh_key(name, public_key, private_key, passphrase) + + if file: + write_file(f'{name}.pem', encode_public_key(public_key, encoding='OpenSSH', key_format='OpenSSH')) + write_file(f'{name}.key', encode_private_key(private_key, encoding='PEM', key_format='OpenSSH', passphrase=passphrase)) -def generate_dh_parameters(name, install=False): +def generate_dh_parameters(name, install=False, file=False): bits = ask_input('Enter DH parameters key size:', default=2048, numeric_only=True) print("Generating parameters...") @@ -534,49 +586,62 @@ def generate_dh_parameters(name, install=False): print("Failed to create DH parameters") return None - if not install: + if not install and not file: print("DH Parameters:") print(encode_dh_parameters(dh_params)) - install_dh_parameters(name, dh_params) + if install: + install_dh_parameters(name, dh_params) + + if file: + write_file(f'{name}.pem', encode_dh_parameters(dh_params)) -def generate_keypair(name, install=False): +def generate_keypair(name, install=False, file=False): private_key, key_type = generate_private_key() public_key = private_key.public_key() passphrase = ask_passphrase() - if not install: + if not install and not file: print(encode_public_key(public_key)) print("") print(encode_private_key(private_key, passphrase=passphrase)) return None - install_keypair(name, key_type, private_key, public_key, passphrase) + if install: + install_keypair(name, key_type, private_key, public_key, passphrase) + + if file: + write_file(f'{name}.pem', encode_public_key(public_key)) + write_file(f'{name}.key', encode_private_key(private_key, passphrase=passphrase)) -def generate_openvpn_key(name, install=False): +def generate_openvpn_key(name, install=False, file=False): result = cmd('openvpn --genkey secret /dev/stdout | grep -o "^[^#]*"') if not result: print("Failed to generate OpenVPN key") return None - if not install: + if not install and not file: print(result) return None - key_lines = result.split("\n") - key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings - key_version = '1' + if install: + key_lines = result.split("\n") + key_data = "".join(key_lines[1:-1]) # Remove wrapper tags and line endings + key_version = '1' + + version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) + if version_search: + key_version = version_search[1] - version_search = re.search(r'BEGIN OpenVPN Static key V(\d+)', result) # Future-proofing (hopefully) - if version_search: - key_version = version_search[1] + 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("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)) + if file: + write_file(f'{name}.key', result) -def generate_wireguard_key(name, install=False): +def generate_wireguard_key(name, install=False, file=False): private_key = cmd('wg genkey') public_key = cmd('wg pubkey', input=private_key) @@ -585,17 +650,26 @@ def generate_wireguard_key(name, install=False): print("Public key: " + public_key) return None - install_wireguard_key(name, private_key, public_key) + if install: + install_wireguard_key(name, private_key, public_key) -def generate_wireguard_psk(name, install=False): + if file: + write_file(f'{name}_public.key', public_key) + write_file(f'{name}_private.key', private_key) + +def generate_wireguard_psk(name, install=False, file=False): psk = cmd('wg genpsk') - if not install: + if not install and not file: print("Pre-shared key:") print(psk) return None - install_wireguard_psk(name, psk) + if install: + install_wireguard_psk(name, psk) + + if file: + write_file(f'{name}.key', psk) # Show functions @@ -721,6 +795,7 @@ if __name__ == '__main__': parser.add_argument('--psk', help='Wireguard pre shared key', required=False) # Global + parser.add_argument('--file', help='Write generated keys into specified filename', action='store_true') parser.add_argument('--install', help='Install generated keys into running-config', action='store_true') args = parser.parse_args() @@ -729,31 +804,31 @@ if __name__ == '__main__': if args.action == 'generate': if args.ca: if args.sign: - generate_ca_certificate_sign(args.ca, args.sign, args.install) + generate_ca_certificate_sign(args.ca, args.sign, install=args.install, file=args.file) else: - generate_ca_certificate(args.ca, args.install) + generate_ca_certificate(args.ca, install=args.install, file=args.file) elif args.certificate: if args.sign: - generate_certificate_sign(args.certificate, args.sign, args.install) + generate_certificate_sign(args.certificate, args.sign, install=args.install, file=args.file) elif args.self_sign: - generate_certificate_selfsign(args.certificate, args.install) + generate_certificate_selfsign(args.certificate, install=args.install, file=args.file) else: generate_certificate_request(name=args.certificate, install=args.install) elif args.crl: - generate_certificate_revocation_list(args.crl, args.install) + generate_certificate_revocation_list(args.crl, install=args.install, file=args.file) elif args.ssh: - generate_ssh_keypair(args.ssh, args.install) + generate_ssh_keypair(args.ssh, install=args.install, file=args.file) elif args.dh: - generate_dh_parameters(args.dh, args.install) + generate_dh_parameters(args.dh, install=args.install, file=args.file) elif args.keypair: - generate_keypair(args.keypair, args.install) + generate_keypair(args.keypair, install=args.install, file=args.file) elif args.openvpn: - generate_openvpn_key(args.openvpn, args.install) + generate_openvpn_key(args.openvpn, install=args.install, file=args.file) elif args.wireguard: if args.key: - generate_wireguard_key(args.key, args.install) + generate_wireguard_key(args.key, install=args.install, file=args.file) elif args.psk: - generate_wireguard_psk(args.psk, args.install) + generate_wireguard_psk(args.psk, install=args.install, file=args.file) elif args.action == 'show': if args.ca: show_certificate_authority(None if args.ca == 'all' else args.ca) diff --git a/src/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py index ff1e3cc56..4df275e04 100755 --- a/src/op_mode/show_dhcp.py +++ b/src/op_mode/show_dhcp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 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 @@ -27,8 +27,7 @@ from datetime import datetime from isc_dhcp_leases import Lease, IscDhcpLeases from vyos.config import Config -from vyos.util import call - +from vyos.util import is_systemd_service_running lease_file = "/config/dhcpd.leases" pool_key = "shared-networkname" @@ -217,7 +216,7 @@ if __name__ == '__main__': exit(0) # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if call('systemctl -q is-active isc-dhcp-server.service') != 0: + if not is_systemd_service_running('isc-dhcp-server.service'): print("WARNING: DHCP server is configured but not started. Data may be stale.") if args.leases: diff --git a/src/op_mode/show_dhcpv6.py b/src/op_mode/show_dhcpv6.py index f70f04298..1f987ff7b 100755 --- a/src/op_mode/show_dhcpv6.py +++ b/src/op_mode/show_dhcpv6.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 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 @@ -27,7 +27,7 @@ from datetime import datetime from isc_dhcp_leases import Lease, IscDhcpLeases from vyos.config import Config -from vyos.util import call +from vyos.util import is_systemd_service_running lease_file = "/config/dhcpdv6.leases" pool_key = "shared-networkname" @@ -202,7 +202,7 @@ if __name__ == '__main__': exit(0) # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if call('systemctl -q is-active isc-dhcp-server6.service') != 0: + if not is_systemd_service_running('isc-dhcp-server6.service'): print("WARNING: DHCPv6 server is configured but not started. Data may be stale.") if args.leases: diff --git a/src/op_mode/show_vrf.py b/src/op_mode/show_vrf.py index 94358c6e4..3c7a90205 100755 --- a/src/op_mode/show_vrf.py +++ b/src/op_mode/show_vrf.py @@ -20,12 +20,11 @@ from json import loads from vyos.util import cmd -vrf_out_tmpl = """ -VRF name state mac address flags interfaces +vrf_out_tmpl = """VRF name state mac address flags interfaces -------- ----- ----------- ----- ---------- -{% for v in vrf %} +{%- for v in vrf %} {{"%-16s"|format(v.ifname)}} {{ "%-8s"|format(v.operstate | lower())}} {{"%-17s"|format(v.address | lower())}} {{ v.flags|join(',')|lower()}} {{v.members|join(',')|lower()}} -{% endfor %} +{%- endfor %} """ diff --git a/src/op_mode/wireguard.py b/src/op_mode/wireguard.py deleted file mode 100755 index e08bc983a..000000000 --- a/src/op_mode/wireguard.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018-2020 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 <http://www.gnu.org/licenses/>. - -import argparse -import os -import sys -import shutil -import syslog as sl -import re - -from vyos.config import Config -from vyos.ifconfig import WireGuardIf -from vyos.util import cmd -from vyos.util import run -from vyos.util import check_kmod -from vyos import ConfigError - -dir = r'/config/auth/wireguard' -psk = dir + '/preshared.key' - -k_mod = 'wireguard' - -def generate_keypair(pk, pub): - """ generates a keypair which is stored in /config/auth/wireguard """ - old_umask = os.umask(0o027) - if run(f'wg genkey | tee {pk} | wg pubkey > {pub}') != 0: - raise ConfigError("wireguard key-pair generation failed") - else: - sl.syslog( - sl.LOG_NOTICE, "new keypair wireguard key generated in " + dir) - os.umask(old_umask) - - -def genkey(location): - """ helper function to check, regenerate the keypair """ - pk = "{}/private.key".format(location) - pub = "{}/public.key".format(location) - old_umask = os.umask(0o027) - if os.path.exists(pk) and os.path.exists(pub): - try: - choice = input( - "You already have a wireguard key-pair, do you want to re-generate? [y/n] ") - if choice == 'y' or choice == 'Y': - generate_keypair(pk, pub) - except KeyboardInterrupt: - sys.exit(0) - else: - """ if keypair is bing executed from a running iso """ - if not os.path.exists(location): - run(f'sudo mkdir -p {location}') - run(f'sudo chgrp vyattacfg {location}') - run(f'sudo chmod 750 {location}') - generate_keypair(pk, pub) - os.umask(old_umask) - - -def showkey(key): - """ helper function to show privkey or pubkey """ - if os.path.exists(key): - print (open(key).read().strip()) - else: - print ("{} not found".format(key)) - - -def genpsk(): - """ - generates a preshared key and shows it on stdout, - it's stored only in the cli config - """ - - psk = cmd('wg genpsk') - print(psk) - -def list_key_dirs(): - """ lists all dirs under /config/auth/wireguard """ - if os.path.exists(dir): - nks = next(os.walk(dir))[1] - for nk in nks: - print (nk) - -def del_key_dir(kname): - """ deletes /config/auth/wireguard/<kname> """ - kdir = "{0}/{1}".format(dir,kname) - if not os.path.isdir(kdir): - print ("named keypair {} not found".format(kname)) - return 1 - shutil.rmtree(kdir) - - -if __name__ == '__main__': - check_kmod(k_mod) - parser = argparse.ArgumentParser(description='wireguard key management') - parser.add_argument( - '--genkey', action="store_true", help='generate key-pair') - parser.add_argument( - '--showpub', action="store_true", help='shows public key') - parser.add_argument( - '--showpriv', action="store_true", help='shows private key') - parser.add_argument( - '--genpsk', action="store_true", help='generates preshared-key') - parser.add_argument( - '--location', action="store", help='key location within {}'.format(dir)) - parser.add_argument( - '--listkdir', action="store_true", help='lists named keydirectories') - parser.add_argument( - '--delkdir', action="store_true", help='removes named keydirectories') - parser.add_argument( - '--showinterface', action="store", help='shows interface details') - args = parser.parse_args() - - try: - if args.genkey: - if args.location: - genkey("{0}/{1}".format(dir, args.location)) - else: - genkey("{}/default".format(dir)) - if args.showpub: - if args.location: - showkey("{0}/{1}/public.key".format(dir, args.location)) - else: - showkey("{}/default/public.key".format(dir)) - if args.showpriv: - if args.location: - showkey("{0}/{1}/private.key".format(dir, args.location)) - else: - showkey("{}/default/private.key".format(dir)) - if args.genpsk: - genpsk() - if args.listkdir: - list_key_dirs() - if args.showinterface: - try: - intf = WireGuardIf(args.showinterface, create=False, debug=False) - print(intf.operational.show_interface()) - # the interface does not exists - except Exception: - pass - if args.delkdir: - if args.location: - del_key_dir(args.location) - else: - del_key_dir("default") - - except ConfigError as e: - print(e) - sys.exit(1) diff --git a/src/op_mode/wireguard_client.py b/src/op_mode/wireguard_client.py index 7a620a01e..7661254da 100755 --- a/src/op_mode/wireguard_client.py +++ b/src/op_mode/wireguard_client.py @@ -38,7 +38,7 @@ To enable this configuration on a VyOS router you can use the following commands {% for addr in address if address is defined %} set interfaces wireguard {{ interface }} peer {{ name }} allowed-ips '{{ addr }}' {% endfor %} -set interfaces wireguard {{ interface }} peer {{ name }} pubkey '{{ pubkey }}' +set interfaces wireguard {{ interface }} peer {{ name }} public-key '{{ pubkey }}' """ client_config = """ |