Skip to content

Commit b64f837

Browse files
committed
[feature] Add Wireguard support #185
Related to #185
1 parent a0cda13 commit b64f837

15 files changed

Lines changed: 367 additions & 5 deletions

File tree

docs/source/backends/wireguard.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=================
2+
Wireguard Backend
3+
=================
4+
5+
.. include:: ../_github.rst
6+
7+
The ``Wireguard`` backend allows to generate Wireguard compatible configurations.
8+
9+
This backend is designed to allow updating the configuration of a wireguard peer
10+
automatically when the configuration of a VPN server is changed in OpenWISP, or
11+
when a new peer is added to OpenWISP (a new device with wireguard VPN client template).
12+
13+
TODO...

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Contents:
5959
/backends/openwrt
6060
/backends/openwisp
6161
/backends/openvpn
62+
/backends/wireguard
6263
/backends/create_your_backend
6364
/general/commandline_utility
6465
/general/running_tests

netjsonconfig/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .backends.openvpn.openvpn import OpenVpn # noqa
66
from .backends.openwisp.openwisp import OpenWisp # noqa
77
from .backends.openwrt.openwrt import OpenWrt # noqa
8+
from .backends.wireguard.wireguard import Wireguard # noqa
89
from .version import VERSION, __version__, get_version # noqa
910

1011

@@ -13,6 +14,7 @@ def get_backends():
1314
'openwrt': OpenWrt,
1415
'openwisp': OpenWisp,
1516
'openvpn': OpenVpn,
17+
'wireguard': Wireguard,
1618
}
1719
logger = logging.getLogger(__name__)
1820

netjsonconfig/backends/openwrt/converters/interfaces.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def __intermediate_addresses(self, interface):
4545
converts NetJSON address to
4646
UCI intermediate data structure
4747
"""
48+
if interface.get('proto') == 'wireguard':
49+
return self.__intermediate_wg_addresses(interface)
4850
address_list = self.get_copy(interface, 'addresses')
4951
# do not ignore interfaces if they do not contain any address
5052
if not address_list:
@@ -83,14 +85,20 @@ def __intermediate_addresses(self, interface):
8385
result += dhcp
8486
return result
8587

88+
def __intermediate_wg_addresses(self, interface):
89+
address_list = interface.pop('wg_addresses')
90+
static = {'addresses': address_list, 'proto': interface['proto']}
91+
return [static]
92+
8693
def __intermediate_interface(self, interface, uci_name):
8794
"""
8895
converts NetJSON interface to
8996
UCI intermediate data structure
9097
"""
91-
interface.update(
92-
{'.type': 'interface', '.name': uci_name, 'ifname': interface.pop('name')}
93-
)
98+
interface.update({'.type': 'interface', '.name': uci_name})
99+
name = interface.pop('name')
100+
if interface.get('proto') != 'wireguard':
101+
interface['ifname'] = name
94102
if 'network' in interface:
95103
del interface['network']
96104
if 'mac' in interface:

netjsonconfig/backends/wireguard/__init__.py

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from ..base.converter import BaseConverter
2+
from .schema import schema
3+
4+
5+
class Wireguard(BaseConverter):
6+
netjson_key = 'wireguard'
7+
intermediate_key = 'wireguard'
8+
_schema = schema
9+
10+
def to_intermediate_loop(self, block, result, index=None):
11+
vpn = self.__intermediate_vpn(block)
12+
result.setdefault('wireguard', [])
13+
result['wireguard'].append(vpn)
14+
return result
15+
16+
def __intermediate_vpn(self, config, remove=None):
17+
config['ListenPort'] = config.pop('port')
18+
config['PrivateKey'] = config.pop('private_key')
19+
config['peers'] = self.__intermediate_peers(config.get('peers', []))
20+
return self.sorted_dict(config)
21+
22+
def __intermediate_peers(self, peers):
23+
peer_list = []
24+
for peer in peers:
25+
peer['AllowedIPs'] = peer.pop('allowed_ips')
26+
peer['PublicKey'] = peer.pop('public_key')
27+
peer['Endpoint'] = peer.pop('endpoint', None)
28+
peer['PreSharedKey'] = peer.pop('preshared_key', None)
29+
peer_list.append(self.sorted_dict(peer))
30+
return peer_list
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import re
2+
3+
from ..base.parser import BaseParser
4+
5+
vpn_pattern = re.compile('^# wireguard config:\s', flags=re.MULTILINE)
6+
config_pattern = re.compile('^([^\s]*) ?(.*)$')
7+
config_suffix = '.conf'
8+
9+
10+
class WireguardParser(BaseParser):
11+
def parse_text(self, config):
12+
raise NotImplementedError()
13+
14+
def parse_tar(self, tar):
15+
raise NotImplementedError()
16+
17+
def _get_vpns(self, text):
18+
raise NotImplementedError()
19+
20+
def _get_config(self, contents):
21+
raise NotImplementedError()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from ..base.renderer import BaseRenderer
2+
3+
4+
class WireguardRenderer(BaseRenderer):
5+
"""
6+
Wireguard Renderer
7+
"""
8+
9+
def cleanup(self, output):
10+
# remove indentations
11+
output = output.replace(' ', '')
12+
# remove last newline
13+
if output.endswith('\n\n'):
14+
output = output[0:-1]
15+
return output
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
Wireguard specific JSON-Schema definition
3+
"""
4+
from copy import deepcopy
5+
6+
from ...schema import schema as default_schema
7+
8+
base_wireguard_schema = {
9+
"$schema": "http://json-schema.org/draft-04/schema#",
10+
"type": "object",
11+
"additionalProperties": True,
12+
"properties": {
13+
"wireguard": {
14+
"type": "array",
15+
"title": "Wireguard",
16+
"uniqueItems": True,
17+
"additionalItems": True,
18+
"propertyOrder": 12,
19+
"items": {
20+
"type": "object",
21+
"title": "Wireguard tunnel",
22+
"additionalProperties": True,
23+
"required": ["name", "port", "private_key"],
24+
"properties": {
25+
"name": {
26+
"title": "interface name",
27+
"description": "Wireguard interface name",
28+
"type": "string",
29+
"minLength": 2,
30+
"maxLength": 15,
31+
"pattern": "^[^\\s]*$",
32+
"propertyOrder": 1,
33+
},
34+
"port": {
35+
"title": "port",
36+
"type": "integer",
37+
"default": 51820,
38+
"maximum": 65535,
39+
"minimum": 1,
40+
"propertyOrder": 2,
41+
},
42+
"private_key": {
43+
"title": "private key",
44+
"type": "string",
45+
"minLength": 44,
46+
"maxLength": 44,
47+
"pattern": "^[^\\s]*$",
48+
"propertyOrder": 3,
49+
},
50+
"peers": {
51+
"type": "array",
52+
"title": "Peers",
53+
"uniqueItems": True,
54+
"additionalItems": True,
55+
"propertyOrder": 11,
56+
"items": {
57+
"type": "object",
58+
"title": "Peer",
59+
"required": ["public_key", "allowed_ips"],
60+
"properties": {
61+
"public_key": {
62+
"title": "public key",
63+
"type": "string",
64+
"minLength": 44,
65+
"maxLength": 44,
66+
"pattern": "^[^\\s]*$",
67+
"propertyOrder": 1,
68+
},
69+
"allowed_ips": {
70+
"title": "public key",
71+
"type": "string",
72+
"propertyOrder": 2,
73+
},
74+
"endpoint": {
75+
"title": "public key",
76+
"type": "string",
77+
"propertyOrder": 3,
78+
},
79+
"preshared_key": {
80+
"title": "pre-shared key",
81+
"type": "string",
82+
"minLength": 44,
83+
"maxLength": 44,
84+
"pattern": "^[^\\s]*$",
85+
"propertyOrder": 4,
86+
},
87+
},
88+
},
89+
},
90+
},
91+
},
92+
}
93+
},
94+
}
95+
96+
schema = deepcopy(base_wireguard_schema)
97+
schema['properties']['files'] = default_schema['properties']['files']
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{% for vpn in data.wireguard %}
2+
# wireguard config: {{ vpn.name }}
3+
4+
[Interface]
5+
{% for key, value in vpn.items() %}
6+
{% if key not in ['name', 'peers'] %}
7+
{{ key }} = {{ value }}
8+
{% endif %}
9+
{% endfor %}
10+
11+
{% for peer in vpn.peers %}
12+
[Peer]
13+
{% for key, value in peer.items() %}
14+
{% if value %}
15+
{{ key }} = {{ value }}
16+
{% endif %}
17+
{% endfor %}
18+
19+
{% endfor %}
20+
{% endfor %}

0 commit comments

Comments
 (0)