Page MenuHomeVyOS Platform

interfaces-wireguard.py
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

interfaces-wireguard.py

#!/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 sys
import os
import re
from copy import deepcopy
from netifaces import interfaces
from vyos import ConfigError
from vyos.config import Config
from vyos.configdict import list_diff
from vyos.util import run
from vyos.ifconfig import WireGuardIf
kdir = r'/config/auth/wireguard'
def _check_kmod():
if not os.path.exists('/sys/module/wireguard'):
if run('modprobe wireguard') != 0:
raise ConfigError("modprobe wireguard failed")
def _migrate_default_keys():
if os.path.exists(f'{kdir}/private.key') and not os.path.exists(f'{kdir}/default/private.key'):
old_umask = os.umask(0o027)
location = f'{kdir}/default'
run(f'sudo mkdir -p {location}')
run(f'sudo chgrp vyattacfg {location}')
run(f'sudo chmod 750 {location}')
os.rename(f'{kdir}/private.key', f'{location}/private.key')
os.rename(f'{kdir}/public.key', f'{location}/public.key')
os.umask(old_umask)
def get_config():
c = Config()
if not c.exists(['interfaces', 'wireguard']):
return None
# determine tagNode instance
if 'VYOS_TAGNODE_VALUE' not in os.environ:
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
dflt_cnf = {
'intfc': '',
'addr': [],
'addr_remove': [],
'descr': '',
'lport': None,
'delete': False,
'state': 'up',
'fwmark': 0x00,
'mtu': 1420,
'peer': {},
'peer_remove': [],
'pk': '{}/default/private.key'.format(kdir)
}
ifname = str(os.environ['VYOS_TAGNODE_VALUE'])
wg = deepcopy(dflt_cnf)
wg['intfc'] = ifname
wg['descr'] = ifname
c.set_level(['interfaces', 'wireguard'])
# interface removal state
if not c.exists(ifname) and c.exists_effective(ifname):
wg['delete'] = True
if not wg['delete']:
c.set_level(['interfaces', 'wireguard', ifname])
if c.exists(['address']):
wg['addr'] = c.return_values(['address'])
# determine addresses which need to be removed
eff_addr = c.return_effective_values(['address'])
wg['addr_remove'] = list_diff(eff_addr, wg['addr'])
# ifalias description
if c.exists(['description']):
wg['descr'] = c.return_value(['description'])
# link state
if c.exists(['disable']):
wg['state'] = 'down'
# local port to listen on
if c.exists(['port']):
wg['lport'] = c.return_value(['port'])
# fwmark value
if c.exists(['fwmark']):
wg['fwmark'] = c.return_value(['fwmark'])
# mtu
if c.exists('mtu'):
wg['mtu'] = c.return_value('mtu')
# private key
if c.exists(['private-key']):
wg['pk'] = "{0}/{1}/private.key".format(
kdir, c.return_value(['private-key']))
# peer removal, wg identifies peers by its pubkey
peer_eff = c.list_effective_nodes(['peer'])
peer_rem = list_diff(peer_eff, c.list_nodes(['peer']))
for p in peer_rem:
wg['peer_remove'].append(
c.return_effective_value(['peer', p, 'pubkey']))
# peer settings
if c.exists(['peer']):
for p in c.list_nodes(['peer']):
if not c.exists(['peer', p, 'disable']):
wg['peer'].update(
{
p: {
'allowed-ips': [],
'address': '',
'port': '',
'pubkey': ''
}
}
)
# peer allowed-ips
if c.exists(['peer', p, 'allowed-ips']):
wg['peer'][p]['allowed-ips'] = c.return_values(
['peer', p, 'allowed-ips'])
# peer address
if c.exists(['peer', p, 'address']):
wg['peer'][p]['address'] = c.return_value(
['peer', p, 'address'])
# peer port
if c.exists(['peer', p, 'port']):
wg['peer'][p]['port'] = c.return_value(
['peer', p, 'port'])
# persistent-keepalive
if c.exists(['peer', p, 'persistent-keepalive']):
wg['peer'][p]['persistent-keepalive'] = c.return_value(
['peer', p, 'persistent-keepalive'])
# preshared-key
if c.exists(['peer', p, 'preshared-key']):
wg['peer'][p]['psk'] = c.return_value(
['peer', p, 'preshared-key'])
# peer pubkeys
key_eff = c.return_effective_value(['peer', p, 'pubkey'])
key_cfg = c.return_value(['peer', p, 'pubkey'])
wg['peer'][p]['pubkey'] = key_cfg
# on a pubkey change we need to remove the pubkey first
# peers are identified by pubkey, so key update means
# peer removal and re-add
if key_eff != key_cfg and key_eff != None:
wg['peer_remove'].append(key_cfg)
# if a peer is disabled, we have to exec a remove for it's pubkey
else:
peer_key = c.return_value(['peer', p, 'pubkey'])
wg['peer_remove'].append(peer_key)
return wg
def verify(c):
if not c:
return None
if not os.path.exists(c['pk']):
raise ConfigError(
"No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'")
if not c['delete']:
if not c['addr']:
raise ConfigError("ERROR: IP address required")
if not c['peer']:
raise ConfigError("ERROR: peer required")
for p in c['peer']:
if not c['peer'][p]['allowed-ips']:
raise ConfigError("ERROR: allowed-ips required for peer " + p)
if not c['peer'][p]['pubkey']:
raise ConfigError("peer pubkey required for peer " + p)
def apply(c):
# no wg configs left, remove all interface from system
# maybe move it into ifconfig.py
if not c:
net_devs = os.listdir('/sys/class/net/')
for dev in net_devs:
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))
# XXX: we are ignoring any errors here
run(f'ip l d dev {wg_intf} >/dev/null')
return None
# init wg class
intfc = WireGuardIf(c['intfc'])
# single interface removal
if c['delete']:
intfc.remove()
return None
# remove IP addresses
for ip in c['addr_remove']:
intfc.del_addr(ip)
# add IP addresses
for ip in c['addr']:
intfc.add_addr(ip)
# interface mtu
intfc.set_mtu(int(c['mtu']))
# ifalias for snmp from description
intfc.set_alias(str(c['descr']))
# remove peers
if c['peer_remove']:
for pkey in c['peer_remove']:
intfc.remove_peer(pkey)
# peer pubkey
# setting up the wg interface
intfc.config['private-key'] = c['pk']
for p in c['peer']:
# peer pubkey
intfc.config['pubkey'] = str(c['peer'][p]['pubkey'])
# peer allowed-ips
intfc.config['allowed-ips'] = c['peer'][p]['allowed-ips']
# local listen port
if c['lport']:
intfc.config['port'] = c['lport']
# fwmark
if c['fwmark']:
intfc.config['fwmark'] = c['fwmark']
# endpoint
if c['peer'][p]['address'] and c['peer'][p]['port']:
intfc.config['endpoint'] = "{}:{}".format(c['peer'][p]['address'], c['peer'][p]['port'])
# persistent-keepalive
if 'persistent-keepalive' in c['peer'][p]:
intfc.config['keepalive'] = c['peer'][p]['persistent-keepalive']
# maybe move it into ifconfig.py
# preshared-key - needs to be read from a file
if 'psk' in c['peer'][p]:
psk_file = '/config/auth/wireguard/psk'
old_umask = os.umask(0o077)
open(psk_file, 'w').write(str(c['peer'][p]['psk']))
os.umask(old_umask)
intfc.config['psk'] = psk_file
intfc.update()
# interface state
intfc.set_admin_state(c['state'])
return None
if __name__ == '__main__':
try:
_check_kmod()
_migrate_default_keys()
c = get_config()
verify(c)
apply(c)
except ConfigError as e:
print(e)
sys.exit(1)

File Metadata

Mime Type
text/x-script.python
Expires
Mon, Dec 15, 5:35 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3069358
Default Alt Text
interfaces-wireguard.py (9 KB)

Event Timeline