1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Forms for wireguard module.
"""
import base64
import binascii
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import validate_ipv4_address
from django.utils.translation import gettext_lazy as _
KEY_LENGTH = 32
def validate_key(key):
"""Validate a WireGuard public/private/pre-shared key."""
valid = False
if isinstance(key, str):
key = key.encode()
try:
decoded_key = base64.b64decode(key)
if len(decoded_key) == KEY_LENGTH and base64.b64encode(
decoded_key) == key:
valid = True
except binascii.Error:
pass
if not valid:
raise ValidationError(_('Invalid key.'))
def validate_endpoint(endpoint):
"""Validate an endpoint of the form: demo.wireguard.com:12912.
Implemented similar to nm-utils.c::_parse_endpoint().
"""
valid = False
try:
destination, port = endpoint.rsplit(':', maxsplit=1)
port = int(port)
if 1 <= port < ((1 << 16) - 1) and destination:
valid = True
if destination[0] == '[' and (destination[-1] != ']'
or len(destination) < 3):
valid = False
except ValueError:
pass
if not valid:
raise ValidationError('Invalid endpoint.')
class AddClientForm(forms.Form):
"""Form to add client."""
public_key = forms.CharField(
label=_('Public Key'), strip=True,
help_text=_('Public key of the peer. Example: '
'MConEJFIg6+DFHg2J1nn9SNLOSE9KR0ysdPgmPjibEs= .'),
validators=[validate_key])
class AddServerForm(forms.Form):
"""Form to add server."""
peer_endpoint = forms.CharField(
label=_('Endpoint of the server'), strip=True,
help_text=_('Domain name and port in the form "ip:port". Example: '
'demo.wireguard.com:12912 .'),
validators=[validate_endpoint])
peer_public_key = forms.CharField(
label=_('Public key of the server'), strip=True, help_text=_(
'Provided by the server operator, a long string of characters. '
'Example: MConEJFIg6+DFHg2J1nn9SNLOSE9KR0ysdPgmPjibEs= .'),
validators=[validate_key])
ip_address = forms.CharField(
label=_('Client IP address provided by server'), strip=True,
help_text=_('IP address assigned to this machine on the VPN after '
'connecting to the endpoint. This value is usually '
'provided by the server operator. Example: 192.168.0.10.'),
validators=[validate_ipv4_address])
private_key = forms.CharField(
label=_('Private key of this machine'), strip=True, help_text=_(
'Optional. New public/private keys are generated if left blank. '
'Public key can then be provided to the server. This is the '
'recommended way. However, some server operators insist on '
'providing this. Example: '
'MConEJFIg6+DFHg2J1nn9SNLOSE9KR0ysdPgmPjibEs= .'), required=False,
validators=[validate_key])
preshared_key = forms.CharField(
label=_('Pre-shared key'), strip=True, required=False, help_text=_(
'Optional. A shared secret key provided by the server to add an '
'additional layer of security. Fill in only if provided. Example: '
'MConEJFIg6+DFHg2J1nn9SNLOSE9KR0ysdPgmPjibEs=.'),
validators=[validate_key])
default_route = forms.BooleanField(
label=_('Use this connection to send all outgoing traffic'),
required=False, help_text=_(
'Typically checked for a VPN service through which all traffic '
'is sent.'))
def get_settings(self):
"""Return NM settings dict from cleaned data."""
settings = {
'common': {
'type': 'wireguard',
'zone': 'external',
},
'ipv4': {
'method': 'manual',
'address': self.cleaned_data['ip_address'],
'netmask': '',
'gateway': '',
'dns': '',
'second_dns': '',
},
'wireguard': {
'peer_endpoint': self.cleaned_data['peer_endpoint'],
'peer_public_key': self.cleaned_data['peer_public_key'],
'private_key': self.cleaned_data['private_key'],
'preshared_key': self.cleaned_data['preshared_key'],
'default_route': self.cleaned_data['default_route'],
}
}
return settings
|