import datetime import ipaddress import json import re import subprocess import flask import flask_login from . import db from . import ipsets from . import system blueprint = flask.Blueprint('vpn', __name__) wgkey_regex = re.compile(r'^[A-Za-z0-9/+=]{44}$') @blueprint.route('/') @flask_login.login_required def index(): return flask.render_template('vpn/index.html') @blueprint.route('/custom') @flask_login.login_required def custom(): if not flask_login.current_user.is_admin: return flask.Response('forbidden', status=403, mimetype='text/plain') with db.locked(): keys = {ip: data for ip, data in db.read('wireguard').items() if data.get('networks') and not data.get('user')} return flask.render_template('vpn/custom.html', keys=keys, ipsets=ipsets.read().keys()) @blueprint.route('/list') @flask_login.login_required def list(): # Return logged-in user’s keys, marking the key used for current connection (if any). user = flask_login.current_user.get_id() remote_addr = ipaddress.ip_address(flask.request.remote_addr) return flask.jsonify([ data | {'ip': ip, 'active': any(remote_addr in ipaddress.ip_network(addr) for addr in (ip, data.get('ip6')))} for ip, data in db.load('wireguard').items() if data.get('user') == user ]) @blueprint.route('/list-custom') @flask_login.login_required def list_custom(): # Return all custom keys. if not flask_login.current_user.is_admin: return flask.Response('forbidden', status=403, mimetype='text/plain') return flask.jsonify([ data | {'ip': ip} for ip, data in db.load('wireguard').items() if data.get('networks') and not data.get('user') ]) @blueprint.route('/new', methods=('POST',)) @flask_login.login_required def new(): # Each key is assigned a new IPv4 address from the pool settings['wg_net']. # Each key is then assigned a corresponding IPv6 subnet, depending on the amount of surplus addresses available. # For wg_net 10.10.0.0/18 and wg_net6 1234:5678:90ab:cdef::/64, # the key for 10.10.0.10/32 would get 1234:5678:90ab:cdef:a::/80. def ipv4to6(net4, ip4, net6): # Calculate the address and prefix length for the IPv6 network that can be assigned to this key. len4 = (net4.max_prefixlen - net4.prefixlen) len6 = (net6.max_prefixlen - net6.prefixlen) # Make sure the network address ends at a colon. Wastes some addresses but IPv6. assigned = (len6 - len4) - (len6 - len4) % 16 ip6 = (net6.network_address + (index<