|
|
|
import ipaddress
|
|
|
|
import itertools
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from starlette.responses import PlainTextResponse
|
|
|
|
|
|
|
|
import const
|
|
|
|
import models
|
|
|
|
import schemas
|
|
|
|
import middleware
|
|
|
|
import db.wireguard
|
|
|
|
import script.wireguard
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
def generate_ip_address(server: schemas.WGServer, v6):
|
|
|
|
if v6:
|
|
|
|
address_space = set(
|
|
|
|
itertools.islice(ipaddress.ip_network("fd42:42:42::1/64", strict=False).hosts(), 1, 1024)
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
address_space = set(ipaddress.ip_network(f"{server.address}/{server.subnet}", strict=False).hosts())
|
|
|
|
occupied_space = set()
|
|
|
|
|
|
|
|
# Try add server IP to list.
|
|
|
|
try:
|
|
|
|
occupied_space.add(ipaddress.ip_address(server.v6_address if v6 else server.address))
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
for p in server.peers:
|
|
|
|
|
|
|
|
# Try add peer ip to list.
|
|
|
|
try:
|
|
|
|
occupied_space.add(ipaddress.ip_address(p.v6_address if v6 else p.address))
|
|
|
|
except ValueError as e:
|
|
|
|
pass # Ignore invalid addresses. These are out of address_space
|
|
|
|
|
|
|
|
address_space -= occupied_space
|
|
|
|
|
|
|
|
# Select first available address
|
|
|
|
return str(list(sorted(address_space)).pop(0))
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/add", response_model=schemas.WGPeer)
|
|
|
|
def add_peer(
|
|
|
|
peer_add: schemas.WGPeerConfigAdd,
|
|
|
|
sess: Session = Depends(middleware.get_db)
|
|
|
|
):
|
|
|
|
server = schemas.WGServer(interface=peer_add.server_interface).from_db(sess)
|
|
|
|
|
|
|
|
if server is None:
|
|
|
|
raise HTTPException(500, detail="The server-interface '%s' does not exist!" % peer_add.server_interface)
|
|
|
|
|
|
|
|
peer = schemas.WGPeer(server_id=server.id)
|
|
|
|
|
|
|
|
if server.v6_address:
|
|
|
|
peer.v6_address = generate_ip_address(server, v6=True)
|
|
|
|
peer.address = generate_ip_address(server, v6=False)
|
|
|
|
|
|
|
|
# Private public key generation
|
|
|
|
keys = script.wireguard.generate_keys()
|
|
|
|
peer.private_key = keys["private_key"]
|
|
|
|
peer.public_key = keys["public_key"]
|
|
|
|
|
|
|
|
# Set 0.0.0.0/0, ::/0 as default allowed ips
|
|
|
|
peer.allowed_ips = ', '.join(const.PEER_DEFAULT_ALLOWED_IPS)
|
|
|
|
|
|
|
|
# Set unnamed
|
|
|
|
peer.name = "Unnamed" if not peer_add.name else peer_add.name
|
|
|
|
|
|
|
|
peer.dns = server.dns
|
|
|
|
|
|
|
|
peer.configuration = script.wireguard.generate_config(dict(
|
|
|
|
peer=peer,
|
|
|
|
server=server
|
|
|
|
))
|
|
|
|
|
|
|
|
db_peer = models.WGPeer(**peer.dict())
|
|
|
|
sess.add(db_peer)
|
|
|
|
sess.commit()
|
|
|
|
|
|
|
|
# If server is running. Add peer
|
|
|
|
if script.wireguard.is_running(server):
|
|
|
|
script.wireguard.add_peer(server, peer)
|
|
|
|
|
|
|
|
# Update server configuration
|
|
|
|
db.wireguard.server_update_configuration(sess, db_peer.server_id)
|
|
|
|
|
|
|
|
return schemas.WGPeer.from_orm(db_peer)
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/configuration/get_or_add")
|
|
|
|
def get_or_add_peer_return_config(peer_get: schemas.WGPeerConfigGetByName,
|
|
|
|
sess: Session = Depends(middleware.get_db)
|
|
|
|
):
|
|
|
|
server = sess.query(models.WGServer).filter_by(interface=peer_get.server_interface).one()
|
|
|
|
peer = sess.query(models.WGPeer).filter_by(name=peer_get.name, server_id=server.id).all()
|
|
|
|
|
|
|
|
if not peer:
|
|
|
|
return add_peer_get_config(schemas.WGPeerConfigAdd(
|
|
|
|
name=peer_get.name,
|
|
|
|
server_interface=peer_get.server_interface
|
|
|
|
), sess=sess)
|
|
|
|
|
|
|
|
peer = peer[0]
|
|
|
|
|
|
|
|
return PlainTextResponse(peer.configuration)
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/configuration/add")
|
|
|
|
def add_peer_get_config(peer_add: schemas.WGPeerConfigAdd,
|
|
|
|
sess: Session = Depends(middleware.get_db)
|
|
|
|
):
|
|
|
|
wg_peer: schemas.WGPeer = add_peer(peer_add, sess)
|
|
|
|
|
|
|
|
return PlainTextResponse(wg_peer.configuration)
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/delete", response_model=schemas.WGPeer)
|
|
|
|
def delete_peer(
|
|
|
|
peer: schemas.WGPeer,
|
|
|
|
sess: Session = Depends(middleware.get_db)
|
|
|
|
):
|
|
|
|
|
|
|
|
server = sess.query(models.WGServer).filter_by(id=peer.server_id).one()
|
|
|
|
|
|
|
|
if not db.wireguard.peer_remove(sess, peer):
|
|
|
|
raise HTTPException(400, detail="Were not able to delete peer %s (%s)" % (peer.name, peer.public_key))
|
|
|
|
|
|
|
|
if script.wireguard.is_running(schemas.WGServer(interface=server.interface)):
|
|
|
|
script.wireguard.remove_peer(server, peer)
|
|
|
|
|
|
|
|
return peer
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/edit")
|
|
|
|
def edit_peer(
|
|
|
|
peer: schemas.WGPeer,
|
|
|
|
sess: Session = Depends(middleware.get_db)
|
|
|
|
):
|
|
|
|
|
|
|
|
peer = db.wireguard.peer_edit(sess, peer)
|
|
|
|
|
|
|
|
return peer
|