def verify(login): cur_user = os.environ['SUDO_USER'] if cur_user in login['del_users']: raise ConfigError( 'Attempting to delete current user: {}'.format(cur_user)) for user in login['add_users']: for key in user['public_keys']: if not key['type']: raise ConfigError( 'SSH public key type missing for "{}"!'.format( key['name'])) if not key['key']: raise ConfigError('SSH public key for id "{}" missing!'.format( key['name'])) # At lease one RADIUS server must not be disabled if len(login['radius_server']) > 0: fail = True for server in login['radius_server']: if not server['disabled']: fail = False if fail: raise ConfigError('At least one RADIUS server must be active.') return None
def verify(igmp_proxy): # bail out early - looks like removal from running config if igmp_proxy is None: return None # bail out early - service is disabled if igmp_proxy['disable']: return None # at least two interfaces are required, one upstream and one downstream if len(igmp_proxy['interfaces']) < 2: raise ConfigError( 'Must define an upstream and at least 1 downstream interface!') upstream = 0 for interface in igmp_proxy['interfaces']: if interface['name'] not in interfaces(): raise ConfigError('Interface "{}" does not exist'.format( interface['name'])) if "upstream" == interface['role']: upstream += 1 if upstream == 0: raise ConfigError('At least 1 upstream interface is required!') elif upstream > 1: raise ConfigError('Only 1 upstream interface allowed!') return None
def verify(pppoe): if not pppoe: return None # vertify auth settings if pppoe['auth_mode'] == 'local': if not pppoe['local_users']: raise ConfigError( 'PPPoE local auth mode requires local users to be configured!') for user in pppoe['local_users']: username = user['name'] if not user['password']: raise ConfigError( f'Password required for local user "{username}"') # if up/download is set, check that both have a value if user['upload'] and not user['download']: raise ConfigError( f'Download speed value required for local user "{username}"' ) if user['download'] and not user['upload']: raise ConfigError( f'Upload speed value required for local user "{username}"') elif pppoe['auth_mode'] == 'radius': if len(pppoe['radius_server']) == 0: raise ConfigError( 'RADIUS authentication requires at least one server') for radius in pppoe['radius_server']: if not radius['key']: server = radius['server'] raise ConfigError( f'Missing RADIUS secret key for server "{ server }"') if len(pppoe['wins']) > 2: raise ConfigError( 'Not more then two IPv4 WINS name-servers can be configured') if len(pppoe['dnsv4']) > 2: raise ConfigError( 'Not more then two IPv4 DNS name-servers can be configured') if len(pppoe['dnsv6']) > 3: raise ConfigError( 'Not more then three IPv6 DNS name-servers can be configured') # local ippool and gateway settings config checks if pppoe['client_ip_subnets'] or pppoe['client_ip_pool']: if not pppoe['ppp_gw']: raise ConfigError( 'PPPoE server requires local IP to be configured') if pppoe['ppp_gw'] and not pppoe['client_ip_subnets'] and not pppoe[ 'client_ip_pool']: print("Warning: No PPPoE client pool defined") return None
def verify(pim): if pim is None: return None if pim['pim_conf']: # Check interfaces if not pim['pim']['ifaces']: raise ConfigError(f"PIM require defined interfaces!") if not pim['pim']['rp']: raise ConfigError(f"RP address required") # Check unique multicast groups uniq_groups = [] for rp_addr in pim['pim']['rp']: if not pim['pim']['rp'][rp_addr]: raise ConfigError(f"Group should be specified for RP " + rp_addr) for group in pim['pim']['rp'][rp_addr]: if (group in uniq_groups): raise ConfigError(f"Group range " + group + " specified cannot exact match another") # Check, is this multicast group gr_addr = group.split('/') if IPv4Address(gr_addr[0]) < IPv4Address('224.0.0.0'): raise ConfigError(group + " not a multicast group") uniq_groups.extend(pim['pim']['rp'][rp_addr])
def verify(vrf_config): # ensure VRF is not assigned to any interface for vrf in vrf_config['vrf_remove']: if len(vrf['interfaces']) > 0: raise ConfigError( f"VRF {vrf['name']} can not be deleted. It has active member interfaces!" ) if len(vrf['routes']) > 0: raise ConfigError( f"VRF {vrf['name']} can not be deleted. It has active routing protocols!" ) table_ids = [] for vrf in vrf_config['vrf_add']: # table id is mandatory if not vrf['table']: raise ConfigError(f"VRF {vrf['name']} table id is mandatory!") # routing table id can't be changed - OS restriction if vrf['table_mod']: raise ConfigError( f"VRF {vrf['name']} table id modification is not possible!") # VRf routing table ID must be unique on the system if vrf['table'] in table_ids: raise ConfigError( f"VRF {vrf['name']} table id {vrf['table']} is not unique!") table_ids.append(vrf['table']) return None
def verify(dummy): if dummy['deleted']: if dummy['is_bridge_member']: raise ConfigError( (f'Interface "{dummy["intf"]}" cannot be deleted as it is a ' f'member of bridge "{dummy["is_bridge_member"]}"!')) return None if dummy['vrf']: if dummy['vrf'] not in interfaces(): raise ConfigError(f'VRF "{dummy["vrf"]}" does not exist') if dummy['is_bridge_member']: raise ConfigError( (f'Interface "{dummy["intf"]}" cannot be member of VRF ' f'"{dummy["vrf"]}" and bridge "{dummy["is_bridge_member"]}" ' f'at the same time!')) if dummy['is_bridge_member'] and dummy['address']: raise ConfigError( (f'Cannot assign address to interface "{dummy["intf"]}" ' f'as it is a member of bridge "{dummy["is_bridge_member"]}"!')) return None
def verify(relay): if relay is None: return None if relay['disabled']: return None for r in relay['instances']: # we don't have to check this instance when it's disabled if r['disabled']: continue # we certainly require a UDP port to listen to if not r['port']: raise ConfigError( 'UDP broadcast relay "{0}" requires a port number'.format( r['id'])) # Relaying data without two interface is kinda senseless ... if len(r['interfaces']) < 2: raise ConfigError( 'UDP broadcast relay "id {0}" requires at least 2 interfaces'. format(r['id'])) return None
def verify(c): if not c: return None if not c['url'] or not c['port']: raise ConfigError("proxy url and port requires a value") elif c['usr'] and not c['passwd']: raise ConfigError("proxy password requires a value") elif not c['usr'] and c['passwd']: raise ConfigError("proxy username requires a value")
def verify(cert): if cert is None: return None if 'domains' not in cert: raise ConfigError("At least one domain name is required to" " request a letsencrypt certificate.") if 'email' not in cert: raise ConfigError("An email address is required to request" " a letsencrypt certificate.")
def check_cert_file_store(cert_name, file_path, dts_path): if not re.search('^\/config\/.+', file_path): print("Warning: \"" + file_path + "\" lies outside of /config/auth directory. It will not get preserved during image upgrade.") #Checking file existence if not os.path.isfile(file_path): raise ConfigError("L2TP VPN configuration error: Invalid "+cert_name+" \""+file_path+"\"") else: ### Cpy file to /etc/ipsec.d/certs/ /etc/ipsec.d/cacerts/ # todo make check ret = call('cp -f '+file_path+' '+dts_path) if ret: raise ConfigError("L2TP VPN configuration error: Cannot copy "+file_path)
def verify(l2tp): if not l2tp: return None if l2tp['auth_mode'] == 'local': if not l2tp['local_users']: raise ConfigError( 'L2TP local auth mode requires local users to be configured!') for user in l2tp['local_users']: if not user['password']: raise ConfigError(f"Password required for user {user['name']}") elif l2tp['auth_mode'] == 'radius': if len(l2tp['radius_server']) == 0: raise ConfigError( "RADIUS authentication requires at least one server") for radius in l2tp['radius_server']: if not radius['key']: raise ConfigError( f"Missing RADIUS secret for server { radius['key'] }") # check for the existence of a client ip pool if not (l2tp['client_ip_pool'] or l2tp['client_ip_subnets']): raise ConfigError( "set vpn l2tp remote-access client-ip-pool requires subnet or start/stop IP pool" ) # check ipv6 if l2tp['client_ipv6_delegate_prefix'] and not l2tp['client_ipv6_pool']: raise ConfigError( 'IPv6 prefix delegation requires client-ipv6-pool prefix') for prefix in l2tp['client_ipv6_delegate_prefix']: if not prefix['mask']: raise ConfigError( 'Delegation-prefix required for individual delegated networks') if len(l2tp['wins']) > 2: raise ConfigError( 'Not more then two IPv4 WINS name-servers can be configured') if len(l2tp['dnsv4']) > 2: raise ConfigError( 'Not more then two IPv4 DNS name-servers can be configured') if len(l2tp['dnsv6']) > 3: raise ConfigError( 'Not more then three IPv6 DNS name-servers can be configured') return None
def verify(relay): # bail out early - looks like removal from running config if relay is None: return None if 'lo' in relay['interface']: raise ConfigError('DHCP relay does not support the loopback interface.') if len(relay['server']) == 0: raise ConfigError('No DHCP relay server(s) configured.\n' \ 'At least one DHCP relay server required.') return None
def verify(igmp): if igmp is None: return None if igmp['igmp_conf']: # Check interfaces if not igmp['ifaces']: raise ConfigError(f"IGMP require defined interfaces!") # Check, is this multicast group for intfc in igmp['ifaces']: for gr_addr in igmp['ifaces'][intfc]['gr_join']: if IPv4Address(gr_addr) < IPv4Address('224.0.0.0'): raise ConfigError(gr_addr + " not a multicast group")
def verify(config): """Verify configuration""" # check for invalid host # pattern $VAR(@) "^[[:alnum:]][-.[:alnum:]]*[[:alnum:]]$" ; "invalid host name $VAR(@)" if not hostname_regex.match(config["hostname"]): raise ConfigError('Invalid host name ' + config["hostname"]) # pattern $VAR(@) "^.{1,63}$" ; "invalid host-name length" length = len(config["hostname"]) if length < 1 or length > 63: raise ConfigError( 'Invalid host-name length, must be less than 63 characters') return None
def verify(nat): if nat['deleted']: # no need to verify the CLI as NAT is going to be deactivated return None if nat['helper_functions']: if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']): raise Exception('could not determine nftable ruleset handlers') for rule in nat['source']: interface = rule['interface_out'] err_msg = f"Source NAT configuration error in rule {rule['number']}:" if interface and interface not in interfaces(): print( f'NAT configuration warning: interface {interface} does not exist on this system' ) if not rule['interface_out']: raise ConfigError(f'{err_msg} outbound-interface not specified') if rule['translation_address']: addr = rule['translation_address'] if addr != 'masquerade' and not is_addr_assigned(addr): print( f'Warning: IP address {addr} does not exist on the system!' ) # common rule verification verify_rule(rule, err_msg) for rule in nat['destination']: interface = rule['interface_in'] err_msg = f"Destination NAT configuration error in rule {rule['number']}:" if interface and interface not in interfaces(): print( f'NAT configuration warning: interface {interface} does not exist on this system' ) if not rule['interface_in']: raise ConfigError(f'{err_msg} inbound-interface not specified') # common rule verification verify_rule(rule, err_msg) return None
def check_kmod(): """ check if kmod is loaded, if not load it """ if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") if run('sudo modprobe wireguard') != 0: sl.syslog(sl.LOG_ERR, "modprobe wireguard failed") raise ConfigError("modprobe wireguard failed")
def verify(dummy): if dummy['deleted']: interface = dummy['intf'] is_member, bridge = is_bridge_member(interface) if is_member: # can not use a f'' formatted-string here as bridge would not get # expanded in the print statement raise ConfigError('Can not delete interface "{0}" as it ' \ 'is a member of bridge "{1}"!'.format(interface, bridge)) return None vrf_name = dummy['vrf'] if vrf_name and vrf_name not in interfaces(): raise ConfigError(f'VRF "{vrf_name}" does not exist') return None
def get_config(): loopback = deepcopy(default_config_data) conf = Config() # determine tagNode instance if 'VYOS_TAGNODE_VALUE' not in os.environ: raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified') loopback['intf'] = os.environ['VYOS_TAGNODE_VALUE'] # Check if interface has been removed if not conf.exists('interfaces loopback ' + loopback['intf']): loopback['deleted'] = True # set new configuration level conf.set_level('interfaces loopback ' + loopback['intf']) # retrieve configured interface addresses if conf.exists('address'): loopback['address'] = conf.return_values('address') # retrieve interface description if conf.exists('description'): loopback['description'] = conf.return_value('description') # Determine interface addresses (currently effective) - to determine which # address is no longer valid and needs to be removed from the interface eff_addr = conf.return_effective_values('address') act_addr = conf.return_values('address') loopback['address_remove'] = list_diff(eff_addr, act_addr) return loopback
def _check_kmod(): """ load required Kernel modules """ modules = ['nft_nat', 'nft_chain_nat_ipv4'] for module in modules: if not os.path.exists(f'/sys/module/{module}'): if call(f'modprobe {module}') != 0: raise ConfigError(f'Loading Kernel module {module} failed')
def get_ethertype(ethertype_val): if ethertype_val == '0x88A8': return '802.1ad' elif ethertype_val == '0x8100': return '802.1q' else: raise ConfigError('invalid ethertype "{}"'.format(ethertype_val))
def _create(self): cmdline = set() if self.config['remote']: cmdline = ('ifname', 'type', 'remote', 'src_interface', 'vni', 'port') elif self.config['src_address']: cmdline = ('ifname', 'type', 'src_address', 'vni', 'port') elif self.config['group'] and self.config['src_interface']: cmdline = ('ifname', 'type', 'group', 'src_interface', 'vni', 'port') else: ifname = self.config['ifname'] raise ConfigError( f'VXLAN "{ifname}" is missing mandatory underlay interface for a multicast network.' ) cmd = 'ip link' for key in cmdline: value = self.config.get(key, '') if not value: continue cmd += ' {} {}'.format(self.mapping.get(key, key), value) self._cmd(cmd)
def apply(data): vrrp_groups, sync_groups = data if vrrp_groups: # safely rename a temporary file with configuration dict try: dict_file = Path("{}.temp".format(VRRP.location['vyos'])) dict_file.rename(Path(VRRP.location['vyos'])) except Exception as err: print("Unable to rename the file with keepalived config for FIFO pipe: {}".format(err)) if not VRRP.is_running(): print("Starting the VRRP process") ret = call("systemctl restart keepalived.service") else: print("Reloading the VRRP process") ret = call("systemctl reload keepalived.service") if ret != 0: raise ConfigError("keepalived failed to start") else: # VRRP is removed in the commit print("Stopping the VRRP process") call("systemctl stop keepalived.service") os.unlink(VRRP.location['daemon']) return None
def verify(mpls): if mpls is None: return None if mpls['mpls_ldp']: # Requre router-id if not mpls['router_id']: raise ConfigError(f"MPLS ldp router-id is mandatory!") # Requre discovery transport-address if not mpls['ldp']['d_transp_ipv4'] and not mpls['ldp']['d_transp_ipv6']: raise ConfigError(f"MPLS ldp discovery transport address is mandatory!") # Requre interface if not mpls['ldp']['interfaces']: raise ConfigError(f"MPLS ldp interface is mandatory!")
def verify(tftpd): # bail out early - looks like removal from running config if tftpd is None: return None # Configuring allowed clients without a server makes no sense if not tftpd['directory']: raise ConfigError('TFTP root directory must be configured!') if not tftpd['listen']: raise ConfigError('TFTP server listen address must be configured!') for addr in tftpd['listen']: if not is_addr_assigned(addr): print('WARNING: TFTP server listen address {0} not assigned to any interface!'.format(addr)) return None
def verify(mroute): if mroute is None: return None for route in mroute['mroute']: route = route.split('/') if IPv4Address(route[0]) < IPv4Address('224.0.0.0'): raise ConfigError(route + " not a multicast network")
def verify(mdns): # '0' interfaces are possible, think of service deletion. Only '1' is not supported! if len(mdns) == 1: raise ConfigError( 'At least 2 interfaces must be specified but %d given!' % len(mdns)) # For mdns-repeater to work it is essential that the interfaces # have an IP address assigned for intf in mdns: try: netifaces.ifaddresses(intf)[netifaces.AF_INET] except KeyError as e: raise ConfigError('No IP address configured for interface "%s"!' % intf) return None
def verify_rule(rule, err_msg): """ Common verify steps used for both source and destination NAT """ if rule['translation_port'] or rule['dest_port'] or rule['source_port']: if rule['protocol'] not in ['tcp', 'udp', 'tcp_udp']: proto = rule['protocol'] raise ConfigError( f'{err_msg} ports can only be specified when protocol is "tcp", "udp" or "tcp_udp" (currently "{proto}")' ) if '/' in rule['translation_address']: raise ConfigError(f'{err_msg}\n' \ 'Cannot use ports with an IPv4net type translation address as it\n' \ 'statically maps a whole network of addresses onto another\n' \ 'network of addresses') if not rule['translation_address']: raise ConfigError(f'{err_msg} translation address not specified')
def verify(regdom): if regdom['deleted']: return None if not regdom['regdom']: raise ConfigError("Wireless regulatory domain is mandatory.") return None
def verify(ntp): # bail out early - looks like removal from running config if ntp is None: return None # Configuring allowed clients without a server makes no sense if len(ntp['allowed_networks']) and not len(ntp['servers']): raise ConfigError('NTP server not configured') for n in ntp['allowed_networks']: try: addr = ip_network( n['network'] ) break except ValueError: raise ConfigError("{0} does not appear to be a valid IPv4 or IPv6 network, check host bits!".format(n['network'])) return None
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)