diff --git a/debian/changelog b/debian/changelog index 415cee0ff..477ce8a56 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,108 +1,114 @@ +vyos-1x (1.2.0-11) unstable; urgency=low + + * Fix: T1217 - cant delete wireguard wg0 interface + + -- hagbard <vyosdev@derith.de> Wed, 30 Jan 2019 14:54:45 -0800 + vyos-1x (1.2.0-10) unstable; urgency=low * T1178: Scheduled script breaks ability to modify configuration -- hagbard <vyosdev@derith.de> Tue, 22 Jan 2019 13:35:09 -0800 vyos-1x (1.2.0-9) unstable; urgency=low * T1168: Upgrade: 1,1,7 -> 1.2.0-epa2 - keyword change in config -- hagbard <vyosdev@derith.de> Mon, 07 Jan 2019 11:42:49 -0800 vyos-1x (1.2.0-8) unstable; urgency=low * T1162: WireGuard: Unable to modify tunnels - KeyError: 'state' -- hagbard <vyosdev@derith.de> Sun, 06 Jan 2019 15:58:40 -0800 vyos-1x (1.2.0-7) unstable; urgency=low * T1061: Wireguard: Missing option to administrativly shutdown interface -- hagbard <vyosdev@derith.de> Fri, 30 Nov 2018 10:22:41 -0800 vyos-1x (1.2.0-6) unstable; urgency=medium * adding vyos-accel-ppp-ipoe-kmod for T989 -- hagbard <vyosdev@derith.de> Thu, 22 Nov 2018 10:56:15 -0800 vyos-1x (1.2.0-5) unstable; urgency=medium * T835: accel-ppp: pppoe implementation -- hagbard <vyosdev@derith.de> Fri, 09 Nov 2018 10:49:48 -0800 vyos-1x (1.2.0-4) unstable; urgency=medium * T240 adds feature system integrity check -- hagbard <vyosdev@derith.de> Mon, 29 Oct 2018 11:10:18 -0700 vyos-1x (1.2.0-3) unstable; urgency=medium * T933: adding vmac_xmit_base if use_vmac has been chosen to avoid split-brain -- hagbard <vyosdev@derith.de> Thu, 25 Oct 2018 11:14:44 -0700 vyos-1x (1.2.0-2) unstable; urgency=medium * T773: adding wireguard support -- hagbard <vyosdev@derith.de> Sat, 11 Aug 2018 15:51:34 -0700 vyos-1x (1.2.0-1) unstable; urgency=medium * T666, T616: new implementation of the VRRP CLI. -- Daniil Baturin <daniil@baturin.org> Fri, 27 Jul 2018 10:25:52 +0200 vyos-1x (1.0.6) unstable; urgency=medium * T736: Rewrite remote logging (syslog) to XML/Python -- hagbard <vyosdev@derith.de> Tue, 24 Jul 2018 10:59:25 -0700 vyos-1x (1.0.5) unstable; urgency=medium * T606: Error in DNS Forwarder listen-on * T608: Cannot configure broadcast-relay service -- Christian Poessinger <christian@poessinger.com> Thu, 19 Apr 2018 21:16:28 +0200 vyos-1x (1.0.4) unstable; urgency=medium * T560: dns-forwarding: replace dnsmasq with pdns-recursor * T588: Rewrite 'service dns forwarding' in new XML style format -- Christian Poessinger <christian@poessinger.com> Sun, 15 Apr 2018 16:13:32 +0200 vyos-1x (1.0.3) unstable; urgency=medium * T379: Add UDP broadcast relay support * mdns repeater scripts - remove python subprocess * Support setting optional 'type' node in command templates -- Christian Poessinger <christian@poessinger.com> Sat, 06 Jan 2018 13:18:30 +0100 vyos-1x (1.0.2) unstable; urgency=low * Added mdns-repeater configuration nodes -- Christian Poessinger <christian@poessinger.com> Sat, 09 Dec 2017 10:39:35 +0100 vyos-1x (1.0.1) unstable; urgency=low * Added the Python library for reading VyOS configs -- Daniil Baturin <daniil@baturin.org> Thu, 17 Aug 2017 22:22:17 -0400 vyos-1x (1.0.0) unstable; urgency=low * Created the package -- Daniil Baturin <daniil@baturin.org> Thu, 17 Aug 2017 20:17:04 -0400 diff --git a/src/conf_mode/wireguard.py b/src/conf_mode/wireguard.py index 57baaee1c..e893dba47 100755 --- a/src/conf_mode/wireguard.py +++ b/src/conf_mode/wireguard.py @@ -1,363 +1,364 @@ #!/usr/bin/env python3 # # Copyright (C) 2018 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 os import re import syslog as sl import subprocess from vyos.config import Config from vyos import ConfigError dir = r'/config/auth/wireguard' pk = dir + '/private.key' pub = dir + '/public.key' psk_file = r'/tmp/psk' def check_kmod(): if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") if os.system('sudo modprobe wireguard') != 0: sl.syslog(sl.LOG_NOTICE, "modprobe wireguard failed") raise ConfigError("modprobe wireguard failed") def get_config(): c = Config() if not c.exists('interfaces wireguard'): return None c.set_level('interfaces') intfcs = c.list_nodes('wireguard') intfcs_eff = c.list_effective_nodes('wireguard') new_lst = list(set(intfcs) - set(intfcs_eff)) del_lst = list(set(intfcs_eff) - set(intfcs)) config_data = { 'interfaces' : {} } ### setting defaults and determine status of the config for intfc in intfcs: cnf = 'wireguard ' + intfc # default data struct config_data['interfaces'].update( { intfc : { 'addr' : '', 'descr' : intfc, ## snmp ifAlias 'lport' : '', 'status' : 'exists', 'state' : 'enabled', 'mtu' : '1420', 'peer' : {} } } ) ### determine status either delete or create for i in new_lst: config_data['interfaces'][i]['status'] = 'create' for i in del_lst: config_data['interfaces'].update( { i : { 'status': 'delete' } } ) ### based on the status, setup conf values for intfc in intfcs: cnf = 'wireguard ' + intfc if config_data['interfaces'][intfc]['status'] != 'delete': ### addresses if c.exists(cnf + ' address'): config_data['interfaces'][intfc]['addr'] = c.return_values(cnf + ' address') ### interface up/down if c.exists(cnf + ' disable'): config_data['interfaces'][intfc]['state'] = 'disable' ### listen port if c.exists(cnf + ' port'): config_data['interfaces'][intfc]['lport'] = c.return_value(cnf + ' port') ### description if c.exists(cnf + ' description'): config_data['interfaces'][intfc]['descr'] = c.return_value(cnf + ' description') ### mtu if c.exists(cnf + ' mtu'): config_data['interfaces'][intfc]['mtu'] = c.return_value(cnf + ' mtu') ### peers if c.exists(cnf + ' peer'): for p in c.list_nodes(cnf + ' peer'): if not c.exists(cnf + ' peer ' + p + ' disable'): config_data['interfaces'][intfc]['peer'].update( { p : { 'allowed-ips' : [], 'endpoint' : '', 'pubkey' : '' } } ) if c.exists(cnf + ' peer ' + p + ' pubkey'): config_data['interfaces'][intfc]['peer'][p]['pubkey'] = c.return_value(cnf + ' peer ' + p + ' pubkey') if c.exists(cnf + ' peer ' + p + ' allowed-ips'): config_data['interfaces'][intfc]['peer'][p]['allowed-ips'] = c.return_values(cnf + ' peer ' + p + ' allowed-ips') if c.exists(cnf + ' peer ' + p + ' endpoint'): config_data['interfaces'][intfc]['peer'][p]['endpoint'] = c.return_value(cnf + ' peer ' + p + ' endpoint') if c.exists(cnf + ' peer ' + p + ' persistent-keepalive'): config_data['interfaces'][intfc]['peer'][p]['persistent-keepalive'] = c.return_value(cnf + ' peer ' + p + ' persistent-keepalive') if c.exists(cnf + ' peer ' + p + ' preshared-key'): config_data['interfaces'][intfc]['peer'][p]['psk'] = c.return_value(cnf + ' peer ' + p + ' preshared-key') return config_data def verify(c): if not c: return None for i in c['interfaces']: if c['interfaces'][i]['status'] != 'delete': if not c['interfaces'][i]['addr']: raise ConfigError("address required for interface " + i) if not c['interfaces'][i]['peer']: raise ConfigError("peer required on interface " + i) for p in c['interfaces'][i]['peer']: if not c['interfaces'][i]['peer'][p]['allowed-ips']: raise ConfigError("allowed-ips required on interface " + i + " for peer " + p) if not c['interfaces'][i]['peer'][p]['pubkey']: raise ConfigError("pubkey from your peer is mandatory on " + i + " for peer " + p) def apply(c): ### no wg config left, delete all wireguard devices on the os if not c: net_devs = os.listdir('/sys/class/net/') for dev in net_devs: - buf = open('/sys/class/net/' + dev + '/uevent', 'r').read() - if re.search("DEVTYPE=wireguard", buf, re.I|re.M): - wg_intf = re.sub("INTERFACE=", "", re.search("INTERFACE=.*", buf, re.I|re.M).group(0)) - sl.syslog(sl.LOG_NOTICE, "removing interface " + wg_intf) - subprocess.call(['ip l d dev ' + wg_intf + ' >/dev/null'], shell=True) + if os.path.isdir('/sys/class/net/' + dev): + buf = open('/sys/class/net/' + dev + '/uevent', 'r').read() + if re.search("DEVTYPE=wireguard", buf, re.I|re.M): + wg_intf = re.sub("INTERFACE=", "", re.search("INTERFACE=.*", buf, re.I|re.M).group(0)) + sl.syslog(sl.LOG_NOTICE, "removing interface " + wg_intf) + subprocess.call(['ip l d dev ' + wg_intf + ' >/dev/null'], shell=True) return None ### ## find the diffs between effective config an new config ### c_eff = Config() c_eff.set_level('interfaces wireguard') ### link status up/down aka interface disable for intf in c['interfaces']: if not c['interfaces'][intf]['status'] == 'delete': if c['interfaces'][intf]['state'] == 'disable': sl.syslog(sl.LOG_NOTICE, "disable interface " + intf) subprocess.call(['ip l s dev ' + intf + ' down ' + ' &>/dev/null'], shell=True) else: sl.syslog(sl.LOG_NOTICE, "enable interface " + intf) subprocess.call(['ip l s dev ' + intf + ' up ' + ' &>/dev/null'], shell=True) ### deletion of a specific interface for intf in c['interfaces']: if c['interfaces'][intf]['status'] == 'delete': sl.syslog(sl.LOG_NOTICE, "removing interface " + intf) subprocess.call(['ip l d dev ' + intf + ' &>/dev/null'], shell=True) ### peer deletion peer_eff = c_eff.list_effective_nodes( intf + ' peer') peer_cnf = [] try: for p in c['interfaces'][intf]['peer']: peer_cnf.append(p) except KeyError: pass peer_rem = list(set(peer_eff) - set(peer_cnf)) for p in peer_rem: pkey = c_eff.return_effective_value( intf + ' peer ' + p +' pubkey') remove_peer(intf, pkey) ### peer pubkey update ### wg identifies peers by its pubky, so we have to remove the peer first ### it will recreated it then below with the new key from the cli config for p in peer_eff: if p in peer_cnf: ekey = c_eff.return_effective_value( intf + ' peer ' + p +' pubkey') nkey = c['interfaces'][intf]['peer'][p]['pubkey'] if nkey != ekey: sl.syslog(sl.LOG_NOTICE, "peer " + p + ' changed pubkey from ' + ekey + 'to key ' + nkey + ' on interface ' + intf) remove_peer(intf, ekey) ### new config if c['interfaces'][intf]['status'] == 'create': if not os.path.exists(pk): raise ConfigError("No keys found, generate them by executing: \'run generate wireguard keypair\'") subprocess.call(['ip l a dev ' + intf + ' type wireguard 2>/dev/null'], shell=True) for addr in c['interfaces'][intf]['addr']: add_addr(intf, addr) subprocess.call(['ip l set up dev ' + intf + ' mtu ' + c['interfaces'][intf]['mtu'] + ' &>/dev/null'], shell=True) configure_interface(c, intf) ### config updates if c['interfaces'][intf]['status'] == 'exists': ### IP address change addr_eff = re.sub("\'", "", c_eff.return_effective_values(intf + ' address')).split() addr_rem = list(set(addr_eff) - set(c['interfaces'][intf]['addr'])) addr_add = list(set(c['interfaces'][intf]['addr']) - set(addr_eff)) if len(addr_rem) != 0: for addr in addr_rem: del_addr(intf, addr) if len(addr_add) != 0: for addr in addr_add: add_addr(intf, addr) ## mtu update mtu = c['interfaces'][intf]['mtu'] if mtu != 1420: sl.syslog(sl.LOG_NOTICE, "setting mtu to " + mtu + " on " + intf) subprocess.call(['ip l set mtu ' + mtu + ' dev ' + intf + ' &>/dev/null'], shell=True) ### persistent-keepalive for p in c['interfaces'][intf]['peer']: val_eff = "" val = "" try: val = c['interfaces'][intf]['peer'][p]['persistent-keepalive'] except KeyError: pass if c_eff.exists_effective(intf + ' peer ' + p + ' persistent-keepalive'): val_eff = c_eff.return_effective_value(intf + ' peer ' + p + ' persistent-keepalive') ### disable keepalive if val_eff and not val: c['interfaces'][intf]['peer'][p]['persistent-keepalive'] = 0 ### set new keepalive value if not val_eff and val: c['interfaces'][intf]['peer'][p]['persistent-keepalive'] = val ## wg command call configure_interface(c, intf) ### ifalias for snmp from description if c['interfaces'][intf]['status'] != 'delete': descr_eff = c_eff.return_effective_value(intf + ' description') cnf_descr = c['interfaces'][intf]['descr'] if descr_eff != cnf_descr: with open('/sys/class/net/' + str(intf) + '/ifalias', 'w') as fh: fh.write(str(cnf_descr)) def configure_interface(c, intf): for p in c['interfaces'][intf]['peer']: ## config init for wg call wg_config = { 'interface' : intf, 'port' : 0, 'private-key' : pk, 'pubkey' : '', 'psk' : '/dev/null', 'allowed-ips' : [], 'fwmark' : 0x00, 'endpoint' : None, 'keepalive' : 0 } ## mandatory settings wg_config['pubkey'] = c['interfaces'][intf]['peer'][p]['pubkey'] wg_config['allowed-ips'] = c['interfaces'][intf]['peer'][p]['allowed-ips'] ## optional settings # listen-port if c['interfaces'][intf]['lport']: wg_config['port'] = c['interfaces'][intf]['lport'] ## endpoint if c['interfaces'][intf]['peer'][p]['endpoint']: wg_config['endpoint'] = c['interfaces'][intf]['peer'][p]['endpoint'] ## persistent-keepalive if 'persistent-keepalive' in c['interfaces'][intf]['peer'][p]: wg_config['keepalive'] = c['interfaces'][intf]['peer'][p]['persistent-keepalive'] ## preshared-key - is only read from a file, it's called via sudo redirection doesn't work either if 'psk' in c['interfaces'][intf]['peer'][p]: old_umask = os.umask(0o077) open(psk_file, 'w').write(str(c['interfaces'][intf]['peer'][p]['psk'])) os.umask(old_umask) wg_config['psk'] = psk_file ### assemble wg command cmd = "sudo wg set " + intf cmd += " listen-port " + str(wg_config['port']) cmd += " private-key " + wg_config['private-key'] cmd += " peer " + wg_config['pubkey'] cmd += " preshared-key " + wg_config['psk'] cmd += " allowed-ips " for ap in wg_config['allowed-ips']: if ap != wg_config['allowed-ips'][-1]: cmd += ap + "," else: cmd += ap if wg_config['endpoint']: cmd += " endpoint " + wg_config['endpoint'] if wg_config['keepalive'] != 0: cmd += " persistent-keepalive " + wg_config['keepalive'] else: cmd += " persistent-keepalive 0" sl.syslog(sl.LOG_NOTICE, cmd) #print (cmd) subprocess.call([cmd], shell=True) """ remove psk_file """ if os.path.exists(psk_file): os.remove(psk_file) def add_addr(intf, addr): # see https://phabricator.vyos.net/T949 ret = subprocess.call(['ip a a dev ' + intf + ' ' + addr + ' &>/dev/null'], shell=True) sl.syslog(sl.LOG_NOTICE, "ip a a dev " + intf + " " + addr) def del_addr(intf, addr): ret = subprocess.call(['ip a d dev ' + intf + ' ' + addr + ' &>/dev/null'], shell=True) sl.syslog(sl.LOG_NOTICE, "ip a d dev " + intf + " " + addr) def remove_peer(intf, peer_key): cmd = 'sudo wg set ' + str(intf) + ' peer ' + peer_key + ' remove &>/dev/null' ret = subprocess.call([cmd], shell=True) sl.syslog(sl.LOG_NOTICE, "peer " + peer_key + " removed from " + intf) if __name__ == '__main__': try: check_kmod() c = get_config() verify(c) apply(c) except ConfigError as e: print(e) sys.exit(1)