|
|
@ -1,16 +1,21 @@ |
|
|
|
import logging |
|
|
|
import random |
|
|
|
import subprocess |
|
|
|
import tempfile |
|
|
|
|
|
|
|
import requests |
|
|
|
import typing |
|
|
|
import configparser |
|
|
|
|
|
|
|
from sqlalchemy.orm import Session |
|
|
|
|
|
|
|
import const |
|
|
|
import models |
|
|
|
import schemas |
|
|
|
import os |
|
|
|
import re |
|
|
|
|
|
|
|
import ipaddress |
|
|
|
import util |
|
|
|
from database import SessionLocal |
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__) |
|
|
|
|
|
|
@ -30,6 +35,7 @@ class WGPermissionsError(Exception): |
|
|
|
class WGPortAlreadyInUse(Exception): |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
class TempServerFile(): |
|
|
|
def __init__(self, server: schemas.WGServer): |
|
|
|
self.server = server |
|
|
@ -78,7 +84,7 @@ def generate_psk(): |
|
|
|
return subprocess.check_output(const.CMD_WG_COMMAND + ["genpsk"]).decode("utf-8").strip() |
|
|
|
|
|
|
|
|
|
|
|
def start_interface(server: schemas.WGServer): |
|
|
|
def start_interface(server: typing.Union[schemas.WGServer, schemas.WGPeer]): |
|
|
|
with TempServerFile(server) as server_file: |
|
|
|
try: |
|
|
|
# print(*const.CMD_WG_QUICK, "up", server_file) |
|
|
@ -113,7 +119,7 @@ def restart_interface(server: schemas.WGServer): |
|
|
|
def is_running(server: schemas.WGServer): |
|
|
|
try: |
|
|
|
output = _run_wg(server, ["show", server.interface]) |
|
|
|
if output is None: |
|
|
|
if output is None or b'Unable to access interface: No such device' in output: |
|
|
|
return False |
|
|
|
except Exception as e: |
|
|
|
print(e.output) |
|
|
@ -217,3 +223,182 @@ def generate_config(obj: typing.Union[typing.Dict[schemas.WGPeer, schemas.WGServ |
|
|
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def retrieve_client_conf_from_server( |
|
|
|
client_name, |
|
|
|
server_interface, |
|
|
|
server_host, |
|
|
|
server_api_key |
|
|
|
): |
|
|
|
const.CLIENT_NAME = "client-1" |
|
|
|
const.CLIENT_SERVER_INTERFACE = "wg0" |
|
|
|
const.CLIENT_SERVER_HOST = "http://localhost:4200" |
|
|
|
const.CLIENT_API_KEY = "8bae20143fb962930614952d80634822361fd5ab9488053866a56de5881f9d7b" |
|
|
|
|
|
|
|
assert server_interface is not None and \ |
|
|
|
server_host is not None and \ |
|
|
|
server_api_key is not None, "Client configuration is invalid: %s, %s, api-key-is-null?: %s" % ( |
|
|
|
server_interface, |
|
|
|
server_host, |
|
|
|
server_api_key is None |
|
|
|
) |
|
|
|
|
|
|
|
api_get_or_add = f"{server_host}/api/v1/peer/configuration/get_or_add" |
|
|
|
|
|
|
|
response = requests.post(api_get_or_add, json={ |
|
|
|
"server_interface": server_interface, |
|
|
|
"name": client_name |
|
|
|
}, headers={ |
|
|
|
"X-API-Key": server_api_key |
|
|
|
}) |
|
|
|
|
|
|
|
if response.status_code != 200: |
|
|
|
print(response.text) |
|
|
|
raise RuntimeError("Could not retrieve config from server: %s" % (api_get_or_add,)) |
|
|
|
|
|
|
|
return response.text |
|
|
|
|
|
|
|
|
|
|
|
def create_client_config(sess: Session, configuration, client_name): |
|
|
|
|
|
|
|
parser = configparser.ConfigParser() |
|
|
|
parser.read_string(configuration) |
|
|
|
public_key = parser["Peer"]["PublicKey"] |
|
|
|
|
|
|
|
assert len(set(parser.sections()) - {"Interface", "Peer"}) == 0, "Missing Interface or Peer section" |
|
|
|
|
|
|
|
# Parse Server |
|
|
|
# Check if server already exists. |
|
|
|
|
|
|
|
is_new_server = False |
|
|
|
is_new_peer = False |
|
|
|
|
|
|
|
try: |
|
|
|
db_server = sess.query(models.WGServer).filter_by( |
|
|
|
public_key=public_key, |
|
|
|
read_only=1 |
|
|
|
).one() |
|
|
|
except: |
|
|
|
db_server = None |
|
|
|
|
|
|
|
if db_server is None: |
|
|
|
db_server = models.WGServer() |
|
|
|
is_new_server = True |
|
|
|
|
|
|
|
db_server.read_only = 1 |
|
|
|
db_server.public_key = parser["Peer"]["PublicKey"] |
|
|
|
db_server.address = parser["Peer"]["Endpoint"] |
|
|
|
db_server.listen_port = random.randint(69000, 19292009) |
|
|
|
|
|
|
|
db_server.v6_address = "N/A" |
|
|
|
db_server.v6_subnet = 0 |
|
|
|
db_server.address = "N/A" |
|
|
|
db_server.subnet = 0 |
|
|
|
db_server.private_key = "N/A" |
|
|
|
db_server.dns = "N/A" |
|
|
|
db_server.post_up = "N/A" |
|
|
|
db_server.post_down = "N/A" |
|
|
|
db_server.is_running = False |
|
|
|
db_server.configuration = "N/A" |
|
|
|
|
|
|
|
# Parse client |
|
|
|
try: |
|
|
|
db_peer = sess.query(models.WGPeer).filter_by( |
|
|
|
private_key=parser["Interface"]["PrivateKey"], |
|
|
|
read_only=1 |
|
|
|
).one() |
|
|
|
except: |
|
|
|
db_peer = None |
|
|
|
|
|
|
|
if db_peer is None: |
|
|
|
db_peer = models.WGPeer() |
|
|
|
is_new_peer = True |
|
|
|
|
|
|
|
db_peer.read_only = 1 |
|
|
|
db_peer.name = client_name |
|
|
|
|
|
|
|
addresses_split = parser["Interface"]["Address"].split(",") |
|
|
|
assert len(addresses_split) > 0, "Must be at least one address" |
|
|
|
|
|
|
|
for address_with_subnet in addresses_split: |
|
|
|
addr, subnet = address_with_subnet.split("/") |
|
|
|
|
|
|
|
if isinstance(ipaddress.ip_address(addr), ipaddress.IPv4Address): |
|
|
|
db_peer.address = address_with_subnet |
|
|
|
elif isinstance(ipaddress.ip_address(addr), ipaddress.IPv6Address): |
|
|
|
db_peer.v6_address = address_with_subnet |
|
|
|
else: |
|
|
|
raise RuntimeError("Incorrect IP Address: %s, %s" % (addr, subnet)) |
|
|
|
|
|
|
|
db_peer.private_key = parser["Interface"]["PrivateKey"] |
|
|
|
db_peer.public_key = "N/A" |
|
|
|
db_peer.allowed_ips = parser["Peer"]["AllowedIPs"] |
|
|
|
db_peer.configuration = configuration |
|
|
|
db_server.interface = f"client_{db_peer.name}" |
|
|
|
db_server.configuration = configuration |
|
|
|
try: |
|
|
|
db_peer.shared_key = parser["Interface"]["PrivateKey"] |
|
|
|
except KeyError: |
|
|
|
db_peer.shared_key = "N/A" |
|
|
|
|
|
|
|
db_peer.dns = parser["Interface"]["DNS"] |
|
|
|
db_peer.server = db_server |
|
|
|
|
|
|
|
if is_new_server: |
|
|
|
sess.add(db_server) |
|
|
|
else: |
|
|
|
sess.merge(db_server) |
|
|
|
sess.commit() |
|
|
|
|
|
|
|
if is_new_peer: |
|
|
|
sess.add(db_peer) |
|
|
|
else: |
|
|
|
sess.merge(db_peer) |
|
|
|
sess.commit() |
|
|
|
|
|
|
|
if const.CLIENT_START_AUTOMATICALLY and not is_running(db_server): |
|
|
|
start_interface(db_server) |
|
|
|
|
|
|
|
|
|
|
|
def load_environment_clients(sess: Session): |
|
|
|
i = 1 |
|
|
|
while True: |
|
|
|
|
|
|
|
client_name = os.getenv(f"CLIENT_{i}_NAME", None) |
|
|
|
client_server_interface = os.getenv(f"CLIENT_{i}_SERVER_INTERFACE", None) |
|
|
|
client_server_host = os.getenv(f"CLIENT_{i}_SERVER_HOST", None) |
|
|
|
client_api_key = os.getenv(f"CLIENT_{i}_API_KEY", None) |
|
|
|
|
|
|
|
if client_api_key is None or \ |
|
|
|
client_server_interface is None or \ |
|
|
|
client_server_host is None or \ |
|
|
|
client_api_key is None: |
|
|
|
break |
|
|
|
|
|
|
|
_LOGGER.warning( |
|
|
|
f"Found client configuration: name={client_name},siface={client_server_interface},shost={client_server_host}") |
|
|
|
|
|
|
|
config = retrieve_client_conf_from_server( |
|
|
|
client_name=client_name, |
|
|
|
server_interface=client_server_interface, |
|
|
|
server_host=client_server_host, |
|
|
|
server_api_key=client_api_key |
|
|
|
) |
|
|
|
|
|
|
|
create_client_config(sess, configuration=config, client_name=client_name) |
|
|
|
|
|
|
|
i += 1 |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
os.environ["CLIENT_1_NAME"] = "client-1" |
|
|
|
os.environ["CLIENT_1_SERVER_INTERFACE"] = "wg0" |
|
|
|
os.environ["CLIENT_1_SERVER_HOST"] = "http://localhost:4200" |
|
|
|
os.environ["CLIENT_1_API_KEY"] = "8bae20143fb962930614952d80634822361fd5ab9488053866a56de5881f9d7b" |
|
|
|
os.environ["CLIENT_2_NAME"] = "client-2" |
|
|
|
os.environ["CLIENT_2_SERVER_INTERFACE"] = "wg0" |
|
|
|
os.environ["CLIENT_2_SERVER_HOST"] = "http://localhost:4200" |
|
|
|
os.environ["CLIENT_2_API_KEY"] = "8bae20143fb962930614952d80634822361fd5ab9488053866a56de5881f9d7b" |
|
|
|
sess: Session = SessionLocal() |
|
|
|
load_environment_clients(sess) |
|
|
|
sess.close() |
|
|
|