diff options
Diffstat (limited to 'src')
-rwxr-xr-x | src/completion/list_ipoe.py | 16 | ||||
-rwxr-xr-x | src/conf_mode/host_name.py | 16 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-openvpn.py | 290 | ||||
-rwxr-xr-x | src/conf_mode/interfaces-tunnel.py | 7 | ||||
-rwxr-xr-x | src/conf_mode/service-ipoe.py | 93 | ||||
-rwxr-xr-x | src/conf_mode/snmp.py | 20 | ||||
-rwxr-xr-x | src/conf_mode/system-login.py | 8 | ||||
-rw-r--r-- | src/etc/systemd/system/pdns-recursor.service.d/override.conf | 2 | ||||
-rwxr-xr-x | src/op_mode/dns_forwarding_reset.py | 6 | ||||
-rwxr-xr-x | src/op_mode/dns_forwarding_statistics.py | 7 | ||||
-rwxr-xr-x | src/op_mode/ipoe-control.py | 65 | ||||
-rwxr-xr-x | src/op_mode/ppp-server-ctrl.py | 71 | ||||
-rwxr-xr-x | src/op_mode/version.py | 4 | ||||
-rwxr-xr-x | src/system/on-dhcp-event.sh | 2 | ||||
-rwxr-xr-x | src/validators/ipv6 | 3 |
15 files changed, 444 insertions, 166 deletions
diff --git a/src/completion/list_ipoe.py b/src/completion/list_ipoe.py new file mode 100755 index 000000000..c386b46a2 --- /dev/null +++ b/src/completion/list_ipoe.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +import argparse +from vyos.util import popen + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--selector', help='Selector: username|ifname|sid', required=True) + args = parser.parse_args() + + output, err = popen("accel-cmd -p 2002 show sessions {0}".format(args.selector)) + if not err: + res = output.split("\r\n") + # Delete header from list + del res[:2] + print(' '.join(res)) diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py index dd5819f9f..a669580ae 100755 --- a/src/conf_mode/host_name.py +++ b/src/conf_mode/host_name.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# 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 @@ -13,8 +13,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# """ conf-mode script for 'system host-name' and 'system domain-name'. @@ -33,10 +31,7 @@ import vyos.hostsd_client from vyos.config import Config from vyos import ConfigError -from vyos.util import cmd -from vyos.util import call -from vyos.util import run - +from vyos.util import cmd, call, run, process_named_running default_config_data = { 'hostname': 'vyos', @@ -166,12 +161,11 @@ def apply(config): call("systemctl restart rsyslog.service") # If SNMP is running, restart it too - ret = run("pgrep snmpd") - if ret == 0: - call("systemctl restart snmpd.service") + if process_named_running('snmpd'): + call('systemctl restart snmpd.service') # restart pdns if it is used - ret = run('/usr/bin/rec_control ping') + ret = run('/usr/bin/rec_control --socket-dir=/run/powerdns ping') if ret == 0: call('systemctl restart pdns-recursor.service') diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index b42765586..836deb64b 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -19,7 +19,7 @@ import re from copy import deepcopy from sys import exit,stderr -from ipaddress import IPv4Address,IPv4Network,summarize_address_range +from ipaddress import ip_address,ip_network,IPv4Address,IPv4Network,IPv6Address,IPv6Network,summarize_address_range from netifaces import interfaces from time import sleep from shutil import rmtree @@ -53,9 +53,11 @@ default_config_data = { 'ipv6_eui64_prefix': '', 'ipv6_forwarding': 1, 'ipv6_dup_addr_detect': 1, + 'ipv6_local_address': [], + 'ipv6_remote_address': [], 'ping_restart': '60', 'ping_interval': '10', - 'local_address': '', + 'local_address': [], 'local_address_subnet': '', 'local_host': '', 'local_port': '', @@ -65,21 +67,30 @@ default_config_data = { 'persistent_tunnel': False, 'protocol': 'udp', 'redirect_gateway': '', - 'remote_address': '', + 'remote_address': [], 'remote_host': [], 'remote_port': '', 'client': [], 'server_domain': '', 'server_max_conn': '', 'server_dns_nameserver': [], - 'server_pool': False, + 'server_pool': True, 'server_pool_start': '', 'server_pool_stop': '', 'server_pool_netmask': '', 'server_push_route': [], 'server_reject_unconfigured': False, - 'server_subnet': '', - 'server_topology': 'net30', + 'server_subnet': [], + 'server_topology': '', + 'server_ipv6_dns_nameserver': [], + 'server_ipv6_local': '', + 'server_ipv6_prefixlen': '', + 'server_ipv6_remote': '', + 'server_ipv6_pool': True, + 'server_ipv6_pool_base': '', + 'server_ipv6_pool_prefixlen': '', + 'server_ipv6_push_route': [], + 'server_ipv6_subnet': [], 'shared_secret_file': '', 'tls': False, 'tls_auth': '', @@ -119,18 +130,14 @@ def checkCertHeader(header, filename): def getDefaultServer(network, topology, devtype): """ - Gets the default server parameters for a "server" directive. - Currently only IPv4 routed but may be extended to support bridged and/or IPv6 in the future. + Gets the default server parameters for a IPv4 "server" directive. Logic from openvpn's src/openvpn/helper.c. Returns a dict with addresses or False if the input parameters were incorrect. """ - if not (topology and devtype): - return False - if not (devtype == 'tun' or devtype == 'tap'): return False - if not network.prefixlen: + if not network.version == 4: return False elif (devtype == 'tun' and network.prefixlen > 29) or (devtype == 'tap' and network.prefixlen > 30): return False @@ -198,6 +205,11 @@ def get_config(): if intf == openvpn['intf']: openvpn['bridge_member'].append(intf) + # bridged server should not have a pool by default (but can be specified manually) + if openvpn['bridge_member']: + openvpn['server_pool'] = False + openvpn['server_ipv6_pool'] = False + # set configuration level conf.set_level('interfaces openvpn ' + openvpn['intf']) @@ -275,9 +287,15 @@ def get_config(): # Local IP address of tunnel - even as it is a tag node - we can only work # on the first address if conf.exists('local-address'): - openvpn['local_address'] = conf.list_nodes('local-address')[0] - if conf.exists('local-address {} subnet-mask'.format(openvpn['local_address'])): - openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(openvpn['local_address'])) + for tmp in conf.list_nodes('local-address'): + tmp_ip = ip_address(tmp) + if tmp_ip.version == 4: + openvpn['local_address'].append(tmp) + if conf.exists('local-address {} subnet-mask'.format(tmp)): + openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(tmp)) + elif tmp_ip.version == 6: + # input IPv6 address could be expanded so get the compressed version + openvpn['ipv6_local_address'].append(str(tmp_ip)) # Local IP address to accept connections if conf.exists('local-host'): @@ -321,7 +339,12 @@ def get_config(): # IP address of remote end of tunnel if conf.exists('remote-address'): - openvpn['remote_address'] = conf.return_value('remote-address') + for tmp in conf.return_values('remote-address'): + tmp_ip = ip_address(tmp) + if tmp_ip.version == 4: + openvpn['remote_address'].append(tmp) + elif tmp_ip.version == 6: + openvpn['ipv6_remote_address'].append(str(tmp_ip)) # Remote host to connect to (dynamic if not set) if conf.exists('remote-host'): @@ -345,11 +368,18 @@ def get_config(): openvpn['server_topology'] = conf.return_value('server topology') # Server-mode subnet (from which client IPs are allocated) + server_network_v4 = None + server_network_v6 = None if conf.exists('server subnet'): - # server_network is used later in this function - server_network = IPv4Network(conf.return_value('server subnet')) - # convert the network in format: "192.0.2.0 255.255.255.0" for later use in template - openvpn['server_subnet'] = server_network.with_netmask.replace(r'/', ' ') + for tmp in conf.return_values('server subnet'): + tmp_ip = ip_network(tmp) + if tmp_ip.version == 4: + server_network_v4 = tmp_ip + # convert the network to format: "192.0.2.0 255.255.255.0" for later use in template + openvpn['server_subnet'].append(tmp_ip.with_netmask.replace(r'/', ' ')) + elif tmp_ip.version == 6: + server_network_v6 = tmp_ip + openvpn['server_ipv6_subnet'].append(str(tmp_ip)) # Client-specific settings for client in conf.list_nodes('server client'): @@ -358,7 +388,11 @@ def get_config(): data = { 'name': client, 'disable': False, - 'ip': '', + 'ip': [], + 'ipv6_ip': [], + 'ipv6_remote': '', + 'ipv6_push_route': [], + 'ipv6_subnet': [], 'push_route': [], 'subnet': [], 'remote_netmask': '' @@ -369,16 +403,28 @@ def get_config(): data['disable'] = True # IP address of the client - if conf.exists('ip'): - data['ip'] = conf.return_value('ip') + for tmp in conf.return_values('ip'): + tmp_ip = ip_address(tmp) + if tmp_ip.version == 4: + data['ip'].append(tmp) + elif tmp_ip.version == 6: + data['ipv6_ip'].append(str(tmp_ip)) # Route to be pushed to the client - for network in conf.return_values('push-route'): - data['push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' ')) + for tmp in conf.return_values('push-route'): + tmp_ip = ip_network(tmp) + if tmp_ip.version == 4: + data['push_route'].append(tmp_ip.with_netmask.replace(r'/', ' ')) + elif tmp_ip.version == 6: + data['ipv6_push_route'].append(str(tmp_ip)) # Subnet belonging to the client - for network in conf.return_values('subnet'): - data['subnet'].append(IPv4Network(network).with_netmask.replace(r'/', ' ')) + for tmp in conf.return_values('subnet'): + tmp_ip = ip_network(tmp) + if tmp_ip.version == 4: + data['subnet'].append(tmp_ip.with_netmask.replace(r'/', ' ')) + elif tmp_ip.version == 6: + data['ipv6_subnet'].append(str(tmp_ip)) # Append to global client list openvpn['client'].append(data) @@ -388,16 +434,34 @@ def get_config(): # Server client IP pool if conf.exists('server client-ip-pool'): - openvpn['server_pool'] = True + conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ip-pool') + + # enable or disable server_pool where necessary + # default is enabled, or disabled in bridge mode + openvpn['server_pool'] = not conf.exists('disable') - if conf.exists('server client-ip-pool start'): - openvpn['server_pool_start'] = conf.return_value('server client-ip-pool start') + if conf.exists('start'): + openvpn['server_pool_start'] = conf.return_value('start') - if conf.exists('server client-ip-pool stop'): - openvpn['server_pool_stop'] = conf.return_value('server client-ip-pool stop') + if conf.exists('stop'): + openvpn['server_pool_stop'] = conf.return_value('stop') - if conf.exists('server client-ip-pool netmask'): - openvpn['server_pool_netmask'] = conf.return_value('server client-ip-pool netmask') + if conf.exists('netmask'): + openvpn['server_pool_netmask'] = conf.return_value('netmask') + + conf.set_level('interfaces openvpn ' + openvpn['intf']) + + # Server client IPv6 pool + if conf.exists('server client-ipv6-pool'): + conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ipv6-pool') + openvpn['server_ipv6_pool'] = not conf.exists('disable') + if conf.exists('base'): + tmp = conf.return_value('base').split('/') + openvpn['server_ipv6_pool_base'] = str(IPv6Address(tmp[0])) + if 1 < len(tmp): + openvpn['server_ipv6_pool_prefixlen'] = tmp[1] + + conf.set_level('interfaces openvpn ' + openvpn['intf']) # DNS suffix to be pushed to all clients if conf.exists('server domain-name'): @@ -409,12 +473,21 @@ def get_config(): # Domain Name Server (DNS) if conf.exists('server name-server'): - openvpn['server_dns_nameserver'] = conf.return_values('server name-server') + for tmp in conf.return_values('server name-server'): + tmp_ip = ip_address(tmp) + if tmp_ip.version == 4: + openvpn['server_dns_nameserver'].append(tmp) + elif tmp_ip.version == 6: + openvpn['server_ipv6_dns_nameserver'].append(str(tmp_ip)) # Route to be pushed to all clients if conf.exists('server push-route'): - for network in conf.return_values('server push-route'): - openvpn['server_push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' ')) + for tmp in conf.return_values('server push-route'): + tmp_ip = ip_network(tmp) + if tmp_ip.version == 4: + openvpn['server_push_route'].append(tmp_ip.with_netmask.replace(r'/', ' ')) + elif tmp_ip.version == 6: + openvpn['server_ipv6_push_route'].append(str(tmp_ip)) # Reject connections from clients that are not explicitly configured if conf.exists('server reject-unconfigured-clients'): @@ -476,25 +549,50 @@ def get_config(): if not openvpn['tls_dh'] and openvpn['tls_key'] and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']): openvpn['tls_dh'] = 'none' + # set default server topology to net30 + if openvpn['mode'] == 'server' and not openvpn['server_topology']: + openvpn['server_topology'] = 'net30' + # Set defaults where necessary. - # If any of the input parameters are missing or wrong, + # If any of the input parameters are wrong, # this will return False and no defaults will be set. - default_server = getDefaultServer(server_network, openvpn['server_topology'], openvpn['type']) - if default_server: - # server-bridge doesn't require a pool so don't set defaults for it - if not openvpn['bridge_member']: - openvpn['server_pool'] = True - if not openvpn['server_pool_start']: - openvpn['server_pool_start'] = default_server['pool_start'] + if server_network_v4 and openvpn['server_topology'] and openvpn['type']: + default_server = None + default_server = getDefaultServer(server_network_v4, openvpn['server_topology'], openvpn['type']) + if default_server: + # server-bridge doesn't require a pool so don't set defaults for it + if openvpn['server_pool'] and not openvpn['bridge_member']: + if not openvpn['server_pool_start']: + openvpn['server_pool_start'] = default_server['pool_start'] - if not openvpn['server_pool_stop']: - openvpn['server_pool_stop'] = default_server['pool_stop'] + if not openvpn['server_pool_stop']: + openvpn['server_pool_stop'] = default_server['pool_stop'] - if not openvpn['server_pool_netmask']: - openvpn['server_pool_netmask'] = default_server['pool_netmask'] + if not openvpn['server_pool_netmask']: + openvpn['server_pool_netmask'] = default_server['pool_netmask'] + + for client in openvpn['client']: + client['remote_netmask'] = default_server['client_remote_netmask'] + + if server_network_v6: + if not openvpn['server_ipv6_local']: + openvpn['server_ipv6_local'] = server_network_v6[1] + if not openvpn['server_ipv6_prefixlen']: + openvpn['server_ipv6_prefixlen'] = server_network_v6.prefixlen + if not openvpn['server_ipv6_remote']: + openvpn['server_ipv6_remote'] = server_network_v6[2] + + if openvpn['server_ipv6_pool'] and server_network_v6.prefixlen < 112: + if not openvpn['server_ipv6_pool_base']: + openvpn['server_ipv6_pool_base'] = server_network_v6[0x1000] + if not openvpn['server_ipv6_pool_prefixlen']: + openvpn['server_ipv6_pool_prefixlen'] = openvpn['server_ipv6_prefixlen'] for client in openvpn['client']: - client['remote_netmask'] = default_server['client_remote_netmask'] + client['ipv6_remote'] = openvpn['server_ipv6_local'] + + if openvpn['redirect_gateway']: + openvpn['redirect_gateway'] += ' ipv6' return openvpn @@ -549,26 +647,45 @@ def verify(openvpn): raise ConfigError('encryption ncp-ciphers cannot be specified in site-to-site mode, only server or client') if openvpn['mode'] == 'site-to-site' and not openvpn['bridge_member']: - if not openvpn['local_address']: + if not (openvpn['local_address'] or openvpn['ipv6_local_address']): raise ConfigError('Must specify "local-address" or "bridge member interface"') + if len(openvpn['local_address']) > 1 or len(openvpn['ipv6_local_address']) > 1: + raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "local-address"') + + if len(openvpn['remote_address']) > 1 or len(openvpn['ipv6_remote_address']) > 1: + raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "remote-address"') + for host in openvpn['remote_host']: - if host == openvpn['remote_address']: + if host in openvpn['remote_address'] or host in openvpn['ipv6_remote_address']: raise ConfigError('"remote-address" cannot be the same as "remote-host"') + if openvpn['local_address'] and not (openvpn['remote_address'] or openvpn['local_address_subnet']): + raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"') + + if openvpn['remote_address'] and not openvpn['local_address']: + raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"') + + if openvpn['ipv6_local_address'] and not openvpn['ipv6_remote_address']: + raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"') + + if openvpn['ipv6_remote_address'] and not openvpn['ipv6_local_address']: + raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"') + if openvpn['type'] == 'tun': - if not openvpn['remote_address']: + if not (openvpn['remote_address'] or openvpn['ipv6_remote_address']): raise ConfigError('Must specify "remote-address"') - if openvpn['local_address'] == openvpn['remote_address']: + if ( (openvpn['local_address'] and openvpn['local_address'] == openvpn['remote_address']) or + (openvpn['ipv6_local_address'] and openvpn['ipv6_local_address'] == openvpn['ipv6_remote_address']) ): raise ConfigError('"local-address" and "remote-address" cannot be the same') - if openvpn['local_address'] == openvpn['local_host']: + if openvpn['local_host'] in openvpn['local_address'] or openvpn['local_host'] in openvpn['ipv6_local_address']: raise ConfigError('"local-address" cannot be the same as "local-host"') else: # checks for client-server or site-to-site bridged - if openvpn['local_address'] or openvpn['remote_address']: + if openvpn['local_address'] or openvpn['ipv6_local_address'] or openvpn['remote_address'] or openvpn['ipv6_remote_address']: raise ConfigError('Cannot specify "local-address" or "remote-address" in client-server or bridge mode') # @@ -590,8 +707,15 @@ def verify(openvpn): if not openvpn['tls_dh'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']): raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode') + if len(openvpn['server_subnet']) > 1 or len(openvpn['server_ipv6_subnet']) > 1: + raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 server subnet') + + for client in openvpn['client']: + if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1: + raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP') + if openvpn['server_subnet']: - subnet = IPv4Network(openvpn['server_subnet'].replace(' ', '/')) + subnet = IPv4Network(openvpn['server_subnet'][0].replace(' ', '/')) if openvpn['type'] == 'tun' and subnet.prefixlen > 29: raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported') @@ -599,14 +723,13 @@ def verify(openvpn): raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported') for client in openvpn['client']: - if client['ip'] and not IPv4Address(client['ip']) in subnet: - raise ConfigError(f'Client IP "{client["ip"]}" not in server subnet "{subnet}"') + if client['ip'] and not IPv4Address(client['ip'][0]) in subnet: + raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}') else: if not openvpn['bridge_member']: raise ConfigError('Must specify "server subnet" or "bridge member interface" in server mode') - if openvpn['server_pool']: if not (openvpn['server_pool_start'] and openvpn['server_pool_stop']): raise ConfigError('Server client-ip-pool requires both start and stop addresses in bridged mode') @@ -615,17 +738,56 @@ def verify(openvpn): v4PoolStop = IPv4Address(openvpn['server_pool_stop']) if v4PoolStart > v4PoolStop: raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}') - if (int(v4PoolStop) - int(v4PoolStart) >= 65536): - raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop}], maximum is 65536 addresses.') + + v4PoolSize = int(v4PoolStop) - int(v4PoolStart) + if v4PoolSize >= 65536: + raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.') v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop)) for client in openvpn['client']: if client['ip']: for v4PoolNet in v4PoolNets: - if IPv4Address(client['ip']) in v4PoolNet: - print(f'Warning: Client "{client["name"]}" IP {client["ip"]} is in server IP pool, it is not reserved for this client.', + if IPv4Address(client['ip'][0]) in v4PoolNet: + print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.', file=stderr) + if openvpn['server_ipv6_subnet']: + if not openvpn['server_subnet']: + raise ConfigError('IPv6 server requires an IPv4 server subnet') + + if openvpn['server_ipv6_pool']: + if not openvpn['server_pool']: + raise ConfigError('IPv6 server pool requires an IPv4 server pool') + + if int(openvpn['server_ipv6_pool_prefixlen']) >= 112: + raise ConfigError('IPv6 server pool must be larger than /112') + + v6PoolStart = IPv6Address(openvpn['server_ipv6_pool_base']) + v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple + v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536 + if v6PoolSize < v4PoolSize: + raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})') + + v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop)) + for client in openvpn['client']: + if client['ipv6_ip']: + for v6PoolNet in v6PoolNets: + if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet: + print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.', + file=stderr) + + else: + if openvpn['server_ipv6_push_route']: + raise ConfigError('IPv6 push-route requires an IPv6 server subnet') + + for client in openvpn ['client']: + if client['ipv6_ip']: + raise ConfigError(f'Server client "{client["name"]}" IPv6 IP requires an IPv6 server subnet') + if client['ipv6_push_route']: + raise ConfigError(f'Server client "{client["name"]} IPv6 push-route requires an IPv6 server subnet"') + if client['ipv6_subnet']: + raise ConfigError(f'Server client "{client["name"]} IPv6 subnet requires an IPv6 server subnet"') + else: # checks for both client and site-to-site go here if openvpn['server_reject_unconfigured']: diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index c51048aeb..06c2ea29b 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -19,6 +19,7 @@ import netifaces from sys import exit from copy import deepcopy +from netifaces import interfaces from vyos.config import Config from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf @@ -506,6 +507,12 @@ def verify(conf): if ipv6_count and not IP6 in kls.ip: print(f'Should not use IPv6 addresses on tunnel {iftype} {ifname}') + # vrf check + + vrf = options['vrf'] + if vrf and vrf not in interfaces(): + raise ConfigError(f'VRF "{vrf}" does not exist') + # tunnel encapsulation check convert = { diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py index 3a14d92ef..76aa80a10 100755 --- a/src/conf_mode/service-ipoe.py +++ b/src/conf_mode/service-ipoe.py @@ -17,27 +17,18 @@ import os import re -from socket import socket, AF_INET, SOCK_STREAM from sys import exit from time import sleep +from stat import S_IRUSR, S_IWUSR, S_IRGRP from vyos.config import Config from vyos import ConfigError -from vyos.util import run +from vyos.util import call from vyos.template import render -ipoe_cnf_dir = r'/etc/accel-ppp/ipoe' -ipoe_cnf = ipoe_cnf_dir + r'/ipoe.config' - -pidfile = r'/var/run/accel_ipoe.pid' -cmd_port = r'2002' - -chap_secrets = ipoe_cnf_dir + '/chap-secrets' -## accel-pppd -d -c /etc/accel-ppp/pppoe/pppoe.config -p /var/run/accel_pppoe.pid - -if not os.path.exists(ipoe_cnf_dir): - os.makedirs(ipoe_cnf_dir) +ipoe_conf = '/run/accel-pppd/ipoe.conf' +ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets' def _get_cpu(): @@ -49,33 +40,14 @@ def _get_cpu(): return cpu_cnt -def _chk_con(): - cnt = 0 - s = socket(AF_INET, SOCK_STREAM) - while True: - try: - s.connect(("127.0.0.1", int(cmd_port))) - break - except ConnectionRefusedError: - sleep(0.5) - cnt += 1 - if cnt == 100: - raise("failed to start pppoe server") - break - - -def _accel_cmd(command): - return run('/usr/bin/accel-cmd -p {cmd_port} {command}') - -##### Inline functions end #### - - def get_config(): c = Config() if not c.exists(['service', 'ipoe-server']): return None - config_data = {} + config_data = { + 'chap_secrets_file' : ipoe_chap_secrets + } c.set_level(['service', 'ipoe-server']) config_data['interfaces'] = {} @@ -215,20 +187,26 @@ def get_config(): return config_data -def generate(c): - if c == None or not c: +def generate(ipoe): + if not ipoe: return None - c['thread_cnt'] = _get_cpu() + dirname = os.path.dirname(ipoe_conf) + if not os.path.exists(dirname): + os.mkdir(dirname) - if c['auth']['mech'] == 'local': - old_umask = os.umask(0o077) - render(chap_secrets, 'ipoe-server/chap-secrets.tmpl', c, trim_blocks=True) - os.umask(old_umask) + ipoe['thread_cnt'] = _get_cpu() + render(ipoe_conf, 'ipoe-server/ipoe.config.tmpl', ipoe, trim_blocks=True) - render(ipoe_cnf, 'ipoe-server/ipoe.config.tmpl', c, trim_blocks=True) - # return c ?? - return c + if ipoe['auth']['mech'] == 'local': + render(ipoe_chap_secrets, 'ipoe-server/chap-secrets.tmpl', ipoe) + os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP) + + else: + if os.path.exists(ipoe_chap_secrets): + os.unlink(ipoe_chap_secrets) + + return None def verify(c): @@ -280,22 +258,19 @@ def verify(c): return c -def apply(c): - if c == None: - if os.path.exists(pidfile): - _accel_cmd('shutdown hard') - if os.path.exists(pidfile): - os.remove(pidfile) +def apply(ipoe): + if ipoe == None: + call('systemctl stop accel-ppp@ipoe.service') + + if os.path.exists(ipoe_conf): + os.unlink(ipoe_conf) + + if os.path.exists(ipoe_chap_secrets): + os.unlink(ipoe_chap_secrets) + return None - if not os.path.exists(pidfile): - ret = run(f'/usr/sbin/accel-pppd -c {ipoe_cnf} -p {pidfile} -d') - _chk_con() - if ret != 0 and os.path.exists(pidfile): - os.remove(pidfile) - raise ConfigError('accel-pppd failed to start') - else: - _accel_cmd('restart') + call('systemctl restart accel-ppp@ipoe.service') if __name__ == '__main__': diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index d654dcb84..7530da2dc 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -535,23 +535,9 @@ def apply(snmp): # start SNMP daemon call("systemctl restart snmpd.service") - # Passwords are not available immediately in the configuration file, - # after daemon startup - we wait until they have been processed by - # snmpd, which we see when a magic line appears in this file. - while True: - while not os.path.exists(config_file_user): - sleep(0.5) - - try: - with open(config_file_user, 'r') as f: - for line in f: - # Search for our magic string inside the file - if 'usmUser' in line: - break - except IOError: - continue - else: - break + while (call('systemctl -q is-active snmpd.service') != 0): + print("service not yet started") + sleep(0.5) # net-snmp is now regenerating the configuration file in the background # thus we need to re-open and re-read the file as the content changed. diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 6008ca0b3..91e2b369f 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -16,6 +16,7 @@ import os +from crypt import crypt, METHOD_SHA512 from psutil import users from pwd import getpwall, getpwnam from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP @@ -52,11 +53,6 @@ def get_local_users(): return local_users - -def get_crypt_pw(password): - return cmd(f'/usr/bin/mkpasswd --method=sha-512 {password}') - - def get_config(): login = default_config_data conf = Config() @@ -204,7 +200,7 @@ def generate(login): # calculate users encrypted password for user in login['add_users']: if user['password_plaintext']: - user['password_encrypted'] = get_crypt_pw(user['password_plaintext']) + user['password_encrypted'] = crypt(user['password_plaintext'], METHOD_SHA512) user['password_plaintext'] = '' # remove old plaintext password diff --git a/src/etc/systemd/system/pdns-recursor.service.d/override.conf b/src/etc/systemd/system/pdns-recursor.service.d/override.conf index 602d7b774..ef4dec303 100644 --- a/src/etc/systemd/system/pdns-recursor.service.d/override.conf +++ b/src/etc/systemd/system/pdns-recursor.service.d/override.conf @@ -2,4 +2,4 @@ WorkingDirectory= WorkingDirectory=/run/powerdns ExecStart= -ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns +ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns --socket-dir=/run/powerdns diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py index 8e2ee546c..bfc640a26 100755 --- a/src/op_mode/dns_forwarding_reset.py +++ b/src/op_mode/dns_forwarding_reset.py @@ -27,6 +27,8 @@ from sys import exit from vyos.config import Config from vyos.util import call +PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' + parser = argparse.ArgumentParser() parser.add_argument("-a", "--all", action="store_true", help="Reset all cache") parser.add_argument("domain", type=str, nargs="?", help="Domain to reset cache entries for") @@ -41,11 +43,11 @@ if __name__ == '__main__': exit(0) if args.all: - call("rec_control wipe-cache \'.$\'") + call(f"{PDNS_CMD} wipe-cache \'.$\'") exit(0) elif args.domain: - call("rec_control wipe-cache \'{0}$\'".format(args.domain)) + call(f"{PDNS_CMD} wipe-cache \'{0}$\'".format(args.domain)) else: parser.print_help() diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py index c400a72cd..8ae92beb7 100755 --- a/src/op_mode/dns_forwarding_statistics.py +++ b/src/op_mode/dns_forwarding_statistics.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 import jinja2 -import sys +from sys import exit from vyos.config import Config from vyos.config import cmd -PDNS_CMD='/usr/bin/rec_control' +PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns' OUT_TMPL_SRC = """ DNS forwarding statistics: @@ -16,13 +16,12 @@ Cache size: {{ cache_size }} kbytes """ - if __name__ == '__main__': # Do nothing if service is not configured c = Config() if not c.exists_effective('service dns forwarding'): print("DNS forwarding is not configured") - sys.exit(0) + exit(0) data = {} diff --git a/src/op_mode/ipoe-control.py b/src/op_mode/ipoe-control.py new file mode 100755 index 000000000..7111498b2 --- /dev/null +++ b/src/op_mode/ipoe-control.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 sys +import argparse + +from vyos.config import Config +from vyos.util import popen, run + +cmd_dict = { + 'cmd_base' : '/usr/bin/accel-cmd -p 2002 ', + 'selector' : ['if', 'username', 'sid'], + 'actions' : { + 'show_sessions' : 'show sessions', + 'show_stat' : 'show stat', + 'terminate' : 'teminate' + } +} + +def is_ipoe_configured(): + if not Config().exists_effective('service ipoe-server'): + print("Service IPoE is not configured") + sys.exit(1) + +def main(): + #parese args + parser = argparse.ArgumentParser() + parser.add_argument('--action', help='Control action', required=True) + parser.add_argument('--selector', help='Selector username|ifname|sid', required=False) + parser.add_argument('--target', help='Target must contain username|ifname|sid', required=False) + args = parser.parse_args() + + + # Check is IPoE configured + is_ipoe_configured() + + if args.action == "restart": + run(cmd_dict['cmd_base'] + "restart") + sys.exit(0) + + if args.action in cmd_dict['actions']: + if args.selector in cmd_dict['selector'] and args.target: + run(cmd_dict['cmd_base'] + "{0} {1} {2}".format(args.action, args.selector, args.target)) + else: + output, err = popen(cmd_dict['cmd_base'] + cmd_dict['actions'][args.action], decode='utf-8') + if not err: + print(output) + else: + print("IPoE server is not running") + +if __name__ == '__main__': + main() diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py new file mode 100755 index 000000000..171107b4a --- /dev/null +++ b/src/op_mode/ppp-server-ctrl.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 sys +import argparse + +from vyos.config import Config +from vyos.util import popen, DEVNULL + +cmd_dict = { + 'cmd_base' : '/usr/bin/accel-cmd -p {} ', + 'vpn_types' : { + 'pppoe' : 2001, + 'pptp' : 2003, + 'l2tp' : 2004, + 'sstp' : 2005 + }, + 'conf_proto' : { + 'pppoe' : 'service pppoe-server', + 'pptp' : 'vpn pptp remote-access', + 'l2tp' : 'vpn l2tp remote-access', + 'sstp' : 'vpn sstp' + } +} + +def is_service_configured(proto): + if not Config().exists_effective(cmd_dict['conf_proto'][proto]): + print("Service {} is not configured".format(proto)) + sys.exit(1) + +def main(): + #parese args + parser = argparse.ArgumentParser() + parser.add_argument('--proto', help='Possible protocols pppoe|pptp|l2tp|sstp', required=True) + parser.add_argument('--action', help='Action command', required=True) + args = parser.parse_args() + + if args.proto in cmd_dict['vpn_types'] and args.action: + # Check is service configured + is_service_configured(args.proto) + + if args.action == "show sessions": + ses_pattern = " ifname,username,ip,ip6,ip6-dp,calling-sid,rate-limit,state,uptime,rx-bytes,tx-bytes" + else: + ses_pattern = "" + + output, err = popen(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][args.proto]) + args.action + ses_pattern, stderr=DEVNULL, decode='utf-8') + if not err: + print(output) + else: + print("{} server is not running".format(args.proto)) + + else: + print("Param --proto and --action required") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/src/op_mode/version.py b/src/op_mode/version.py index 8599c958f..2791f5e5c 100755 --- a/src/op_mode/version.py +++ b/src/op_mode/version.py @@ -53,6 +53,8 @@ def read_file(name): version_output_tmpl = """ Version: VyOS {{version}} +Release Train: {{release_train}} + Built by: {{built_by}} Built on: {{built_on}} Build UUID: {{build_uuid}} @@ -102,7 +104,7 @@ if __name__ == '__main__': elif run(""" grep '^overlay /' /proc/mounts >/dev/null """) != 0: boot_via = "legacy non-image installation" version_data['boot_via'] = boot_via - + # Get hardware details from DMI version_data['hardware_vendor'] = read_file('/sys/class/dmi/id/sys_vendor') diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh index 5046912a6..385ae460f 100755 --- a/src/system/on-dhcp-event.sh +++ b/src/system/on-dhcp-event.sh @@ -63,7 +63,7 @@ if [ $changes -gt 0 ]; then echo Success pid=`pgrep pdns_recursor` if [ -n "$pid" ]; then - sudo rec_control reload-zones + sudo rec_control --socket-dir=/run/powerdns reload-zones fi else echo No changes made diff --git a/src/validators/ipv6 b/src/validators/ipv6 new file mode 100755 index 000000000..f18d4a63e --- /dev/null +++ b/src/validators/ipv6 @@ -0,0 +1,3 @@ +#!/bin/sh + +ipaddrcheck --is-ipv6 $1 |