2024-06-17 21:16:42 +02:00
import itertools
import random
2022-01-02 20:44:27 +01:00
import sqlite3
2021-12-26 00:26:39 +01:00
import configparser
import hashlib
import ipaddress
import json
2024-08-16 00:26:20 +02:00
import traceback
2021-05-04 07:32:34 +02:00
# Python Built-in Library
2020-10-18 07:10:13 +02:00
import os
2021-12-26 00:26:39 +01:00
import secrets
2020-10-18 07:10:13 +02:00
import subprocess
2021-07-02 19:23:04 +02:00
import time
2021-12-28 20:53:51 +01:00
import re
2024-08-17 06:31:46 +02:00
import urllib . error
2024-06-17 21:16:42 +02:00
import uuid
2021-12-28 20:53:51 +01:00
from datetime import datetime , timedelta
2024-06-17 21:40:25 +02:00
from typing import Any
2024-06-17 21:16:42 +02:00
import bcrypt
2021-05-04 07:32:34 +02:00
# PIP installed library
import ifcfg
2024-06-17 21:16:42 +02:00
import psutil
import pyotp
2024-08-16 04:47:44 +02:00
from flask import Flask , request , render_template , session , g
2024-06-17 21:16:42 +02:00
from json import JSONEncoder
2024-07-31 08:27:44 +02:00
from flask_cors import CORS
2024-06-17 21:16:42 +02:00
2021-12-26 00:26:39 +01:00
from icmplib import ping , traceroute
# Import other python files
2022-04-06 03:39:47 +02:00
import threading
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
from flask . json . provider import DefaultJSONProvider
2022-02-11 15:35:58 +01:00
2024-06-17 21:16:42 +02:00
DASHBOARD_VERSION = ' v4.0 '
CONFIGURATION_PATH = os . getenv ( ' CONFIGURATION_PATH ' , ' . ' )
DB_PATH = os . path . join ( CONFIGURATION_PATH , ' db ' )
2022-01-01 00:57:59 +01:00
if not os . path . isdir ( DB_PATH ) :
os . mkdir ( DB_PATH )
2024-06-17 21:16:42 +02:00
DASHBOARD_CONF = os . path . join ( CONFIGURATION_PATH , ' wg-dashboard.ini ' )
2022-02-11 15:35:58 +01:00
2024-06-17 21:16:42 +02:00
# WireGuard's configuration path
WG_CONF_PATH = None
# Dashboard Config Name
2021-05-14 00:00:40 +02:00
# Upgrade Required
2021-12-28 20:53:51 +01:00
UPDATE = None
2021-05-14 00:00:40 +02:00
# Flask App Configuration
2024-08-14 07:17:47 +02:00
2021-09-09 03:56:31 +02:00
app = Flask ( " WGDashboard " )
2021-12-25 20:44:14 +01:00
app . config [ ' SEND_FILE_MAX_AGE_DEFAULT ' ] = 5206928
2024-06-17 21:16:42 +02:00
app . secret_key = secrets . token_urlsafe ( 32 )
2024-08-14 07:17:47 +02:00
2021-09-08 18:39:25 +02:00
2024-06-17 21:16:42 +02:00
class ModelEncoder ( JSONEncoder ) :
def default ( self , o : Any ) - > Any :
if hasattr ( o , ' toJson ' ) :
return o . toJson ( )
else :
return super ( ModelEncoder , self ) . default ( o )
2021-12-26 00:26:39 +01:00
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
'''
Classes
'''
2021-08-14 23:13:16 +02:00
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
def ResponseObject ( status = True , message = None , data = None ) - > Flask . response_class :
response = Flask . make_response ( app , {
" status " : status ,
" message " : message ,
" data " : data
} )
response . content_type = " application/json "
return response
class CustomJsonEncoder ( DefaultJSONProvider ) :
def __init__ ( self , app ) :
super ( ) . __init__ ( app )
def default ( self , o ) :
2024-08-06 16:17:14 +02:00
if ( isinstance ( o , WireguardConfiguration )
or isinstance ( o , Peer )
or isinstance ( o , PeerJob )
or isinstance ( o , Log )
or isinstance ( o , DashboardAPIKey )
or isinstance ( o , PeerShareLink ) ) :
2024-06-17 21:16:42 +02:00
return o . toJson ( )
return super ( ) . default ( self , o )
app . json = CustomJsonEncoder ( app )
2024-07-28 00:51:43 +02:00
class Log :
def __init__ ( self , LogID : str , JobID : str , LogDate : str , Status : str , Message : str ) :
self . LogID = LogID
self . JobID = JobID
self . LogDate = LogDate
self . Status = Status
self . Message = Message
def toJson ( self ) :
return {
" LogID " : self . LogID ,
" JobID " : self . JobID ,
" LogDate " : self . LogDate ,
" Status " : self . Status ,
" Message " : self . Message
}
2024-07-30 00:40:07 +02:00
def __dict__ ( self ) :
return self . toJson ( )
2024-08-03 23:03:39 +02:00
class DashboardLogger :
def __init__ ( self ) :
self . loggerdb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard_log.db ' ) ,
check_same_thread = False )
self . loggerdb . row_factory = sqlite3 . Row
self . loggerdbCursor = self . loggerdb . cursor ( )
self . __createLogDatabase ( )
self . log ( Message = " WGDashboard started " )
def __createLogDatabase ( self ) :
existingTable = self . loggerdbCursor . execute ( " SELECT name from sqlite_master where type= ' table ' " ) . fetchall ( )
existingTable = [ t [ ' name ' ] for t in existingTable ]
if " DashboardLog " not in existingTable :
2024-08-07 06:37:05 +02:00
self . loggerdbCursor . execute (
" CREATE TABLE DashboardLog (LogID VARCHAR NOT NULL, LogDate DATETIME DEFAULT (strftime( ' % Y- % m- %d % H: % M: % S ' , ' now ' , ' localtime ' )), URL VARCHAR, IP VARCHAR, Status VARCHAR, Message VARCHAR, PRIMARY KEY (LogID)) " )
2024-08-03 23:03:39 +02:00
self . loggerdb . commit ( )
def log ( self , URL : str = " " , IP : str = " " , Status : str = " true " , Message : str = " " ) - > bool :
try :
2024-08-07 06:37:05 +02:00
self . loggerdbCursor . execute (
" INSERT INTO DashboardLog (LogID, URL, IP, Status, Message) VALUES (?, ?, ?, ?, ?) " , ( str ( uuid . uuid4 ( ) ) , URL , IP , Status , Message , ) )
2024-08-03 23:03:39 +02:00
self . loggerdb . commit ( )
return True
except Exception as e :
print ( e )
return False
class PeerJobLogger :
2024-07-28 00:51:43 +02:00
def __init__ ( self ) :
self . loggerdb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard_log.db ' ) ,
check_same_thread = False )
self . loggerdb . row_factory = sqlite3 . Row
self . logs : list ( Log ) = [ ]
self . loggerdbCursor = self . loggerdb . cursor ( )
self . __createLogDatabase ( )
def __createLogDatabase ( self ) :
existingTable = self . loggerdbCursor . execute ( " SELECT name from sqlite_master where type= ' table ' " ) . fetchall ( )
existingTable = [ t [ ' name ' ] for t in existingTable ]
2024-06-17 21:16:42 +02:00
2024-07-28 00:51:43 +02:00
if " JobLog " not in existingTable :
2024-07-31 00:45:05 +02:00
self . loggerdbCursor . execute ( " CREATE TABLE JobLog (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME DEFAULT (strftime( ' % Y- % m- %d % H: % M: % S ' , ' now ' , ' localtime ' )), Status VARCHAR NOT NULL, Message VARCHAR, PRIMARY KEY (LogID)) " )
2024-07-28 00:51:43 +02:00
self . loggerdb . commit ( )
def log ( self , JobID : str , Status : bool = True , Message : str = " " ) - > bool :
try :
self . loggerdbCursor . execute ( f " INSERT INTO JobLog (LogID, JobID, Status, Message) VALUES (?, ?, ?, ?) " ,
( str ( uuid . uuid4 ( ) ) , JobID , Status , Message , ) )
self . loggerdb . commit ( )
except Exception as e :
print ( e )
return False
return True
2024-07-30 00:40:07 +02:00
def getLogs ( self , all : bool = False , configName = None ) - > list [ Log ] :
logs : list [ Log ] = [ ]
2024-07-28 00:51:43 +02:00
try :
2024-07-30 00:40:07 +02:00
allJobs = AllPeerJobs . getAllJobs ( configName )
allJobsID = " , " . join ( [ f " ' { x . JobID } ' " for x in allJobs ] )
table = self . loggerdb . execute ( f " SELECT * FROM JobLog WHERE JobID IN ( { allJobsID } ) ORDER BY LogDate DESC " ) . fetchall ( )
2024-07-28 00:51:43 +02:00
self . logs . clear ( )
for l in table :
2024-07-30 00:40:07 +02:00
logs . append (
2024-07-28 00:51:43 +02:00
Log ( l [ " LogID " ] , l [ " JobID " ] , l [ " LogDate " ] , l [ " Status " ] , l [ " Message " ] ) )
except Exception as e :
2024-07-30 00:40:07 +02:00
return logs
return logs
2024-07-28 00:51:43 +02:00
2024-06-17 21:16:42 +02:00
class PeerJob :
def __init__ ( self , JobID : str , Configuration : str , Peer : str ,
Field : str , Operator : str , Value : str , CreationDate : datetime , ExpireDate : datetime , Action : str ) :
self . Action = Action
self . ExpireDate = ExpireDate
self . CreationDate = CreationDate
self . Value = Value
self . Operator = Operator
self . Field = Field
self . Configuration = Configuration
self . Peer = Peer
self . JobID = JobID
def toJson ( self ) :
return {
" JobID " : self . JobID ,
" Configuration " : self . Configuration ,
" Peer " : self . Peer ,
" Field " : self . Field ,
" Operator " : self . Operator ,
" Value " : self . Value ,
" CreationDate " : self . CreationDate ,
" ExpireDate " : self . ExpireDate ,
" Action " : self . Action
}
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
def __dict__ ( self ) :
return self . toJson ( )
class PeerJobs :
def __init__ ( self ) :
self . Jobs : list [ PeerJob ] = [ ]
self . jobdb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard_job.db ' ) ,
check_same_thread = False )
self . jobdb . row_factory = sqlite3 . Row
self . jobdbCursor = self . jobdb . cursor ( )
self . __createPeerJobsDatabase ( )
self . __getJobs ( )
def __getJobs ( self ) :
2024-06-25 17:02:13 +02:00
self . Jobs . clear ( )
2024-06-17 21:16:42 +02:00
jobs = self . jobdbCursor . execute ( " SELECT * FROM PeerJobs WHERE ExpireDate IS NULL " ) . fetchall ( )
for job in jobs :
self . Jobs . append ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
2024-07-30 00:40:07 +02:00
def getAllJobs ( self , configuration : str = None ) :
if configuration is not None :
jobs = self . jobdbCursor . execute (
f " SELECT * FROM PeerJobs WHERE Configuration = ? " , ( configuration , ) ) . fetchall ( )
j = [ ]
for job in jobs :
j . append ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
return j
return [ ]
2024-06-17 21:16:42 +02:00
def __createPeerJobsDatabase ( self ) :
existingTable = self . jobdbCursor . execute ( " SELECT name from sqlite_master where type= ' table ' " ) . fetchall ( )
existingTable = [ t [ ' name ' ] for t in existingTable ]
if " PeerJobs " not in existingTable :
self . jobdbCursor . execute ( '''
CREATE TABLE PeerJobs ( JobID VARCHAR NOT NULL , Configuration VARCHAR NOT NULL , Peer VARCHAR NOT NULL ,
Field VARCHAR NOT NULL , Operator VARCHAR NOT NULL , Value VARCHAR NOT NULL , CreationDate DATETIME ,
ExpireDate DATETIME , Action VARCHAR NOT NULL , PRIMARY KEY ( JobID ) )
''' )
self . jobdb . commit ( )
def toJson ( self ) :
return [ x . toJson ( ) for x in self . Jobs ]
def searchJob ( self , Configuration : str , Peer : str ) :
return list ( filter ( lambda x : x . Configuration == Configuration and x . Peer == Peer , self . Jobs ) )
2024-06-25 17:02:13 +02:00
def saveJob ( self , Job : PeerJob ) - > tuple [ bool , list ] | tuple [ bool , str ] :
try :
if ( len ( str ( Job . CreationDate ) ) ) == 0 :
self . jobdbCursor . execute ( '''
INSERT INTO PeerJobs VALUES ( ? , ? , ? , ? , ? , ? , strftime ( ' % Y- % m- %d % H: % M: % S ' , ' now ' ) , NULL , ? )
''' , (Job.JobID, Job.Configuration, Job.Peer, Job.Field, Job.Operator, Job.Value, Job.Action,))
2024-07-30 04:17:51 +02:00
JobLogger . log ( Job . JobID , Message = f " Job is created if { Job . Field } { Job . Operator } { Job . Value } then { Job . Action } " )
2024-06-25 17:02:13 +02:00
else :
2024-07-30 04:17:51 +02:00
currentJob = self . jobdbCursor . execute ( ' SELECT * FROM PeerJobs WHERE JobID = ? ' , ( Job . JobID , ) ) . fetchone ( )
if currentJob is not None :
self . jobdbCursor . execute ( '''
UPDATE PeerJobs SET Field = ? , Operator = ? , Value = ? , Action = ? WHERE JobID = ?
''' , (Job.Field, Job.Operator, Job.Value, Job.Action, Job.JobID))
JobLogger . log ( Job . JobID ,
Message = f " Job is updated from if { currentJob [ ' Field ' ] } { currentJob [ ' Operator ' ] } { currentJob [ ' value ' ] } then { currentJob [ ' Action ' ] } ; to if { Job . Field } { Job . Operator } { Job . Value } then { Job . Action } " )
2024-06-25 17:02:13 +02:00
self . jobdb . commit ( )
self . __getJobs ( )
2024-07-30 04:17:51 +02:00
2024-06-30 18:58:02 +02:00
return True , list (
filter ( lambda x : x . Configuration == Job . Configuration and x . Peer == Job . Peer and x . JobID == Job . JobID ,
self . Jobs ) )
2024-06-25 17:02:13 +02:00
except Exception as e :
return False , str ( e )
2024-06-30 18:58:02 +02:00
def deleteJob ( self , Job : PeerJob ) - > tuple [ bool , list ] | tuple [ bool , str ] :
try :
if ( len ( str ( Job . CreationDate ) ) ) == 0 :
return False , " Job does not exist "
self . jobdbCursor . execute ( '''
2024-07-28 00:51:43 +02:00
UPDATE PeerJobs SET ExpireDate = strftime ( ' % Y- % m- %d % H: % M: % S ' , ' now ' ) WHERE JobID = ?
2024-06-30 18:58:02 +02:00
''' , (Job.JobID,))
self . jobdb . commit ( )
2024-07-30 04:17:51 +02:00
JobLogger . log ( Job . JobID , Message = f " Job is removed due to being deleted or finshed. " )
2024-06-30 18:58:02 +02:00
self . __getJobs ( )
return True , list (
filter ( lambda x : x . Configuration == Job . Configuration and x . Peer == Job . Peer and x . JobID == Job . JobID ,
self . Jobs ) )
except Exception as e :
return False , str ( e )
def runJob ( self ) :
needToDelete = [ ]
for job in self . Jobs :
c = WireguardConfigurations . get ( job . Configuration )
if c is not None :
f , fp = c . searchPeer ( job . Peer )
if f :
if job . Field in [ " total_receive " , " total_sent " , " total_data " ] :
s = job . Field . split ( " _ " ) [ 1 ]
x : float = getattr ( fp , f " total_ { s } " ) + getattr ( fp , f " cumu_ { s } " )
y : float = float ( job . Value )
else :
x : datetime = datetime . now ( )
2024-08-07 06:37:05 +02:00
y : datetime = datetime . strptime ( job . Value , " % Y- % m- %d % H: % M: % S " )
2024-06-30 18:58:02 +02:00
runAction : bool = self . __runJob_Compare ( x , y , job . Operator )
if runAction :
2024-07-28 00:51:43 +02:00
s = False
2024-06-30 18:58:02 +02:00
if job . Action == " restrict " :
2024-07-28 00:51:43 +02:00
s = c . restrictPeers ( [ fp . id ] ) . get_json ( )
2024-06-30 18:58:02 +02:00
elif job . Action == " delete " :
2024-07-28 00:51:43 +02:00
s = c . deletePeers ( [ fp . id ] ) . get_json ( )
if s [ ' status ' ] is True :
JobLogger . log ( job . JobID , s [ " status " ] ,
f " Peer { fp . id } from { c . Name } is successfully { job . Action } ed. "
)
needToDelete . append ( job )
else :
JobLogger . log ( job . JobID , s [ " status " ] ,
f " Peer { fp . id } from { c . Name } failed { job . Action } ed. "
)
2024-06-30 18:58:02 +02:00
for j in needToDelete :
self . deleteJob ( j )
def __runJob_Compare ( self , x : float | datetime , y : float | datetime , operator : str ) :
if operator == " eq " :
return x == y
if operator == " neq " :
return x != y
if operator == " lgt " :
return x > y
if operator == " lst " :
return x < y
2024-08-06 01:57:17 +02:00
class PeerShareLink :
2024-08-06 16:17:14 +02:00
def __init__ ( self , ShareID : str , Configuration : str , Peer : str , ExpireDate : datetime , ShareDate : datetime ) :
2024-08-06 01:57:17 +02:00
self . ShareID = ShareID
self . Peer = Peer
self . Configuration = Configuration
2024-08-06 16:17:14 +02:00
self . ShareDate = ShareDate
self . ExpireDate = ExpireDate
2024-08-07 01:15:00 +02:00
2024-08-06 01:57:17 +02:00
def toJson ( self ) :
return {
2024-08-06 16:17:14 +02:00
" ShareID " : self . ShareID ,
2024-08-06 01:57:17 +02:00
" Peer " : self . Peer ,
" Configuration " : self . Configuration ,
2024-08-06 16:17:14 +02:00
" ExpireDate " : self . ExpireDate
2024-08-06 01:57:17 +02:00
}
class PeerShareLinks :
def __init__ ( self ) :
self . Links : list [ PeerShareLink ] = [ ]
2024-08-07 01:15:00 +02:00
self . PeerShareLinkCursor = sqldb . cursor ( )
existingTables = self . PeerShareLinkCursor . execute ( " SELECT name FROM sqlite_master WHERE type= ' table ' and name = ' PeerShareLinks ' " ) . fetchall ( )
2024-08-06 01:57:17 +02:00
if len ( existingTables ) == 0 :
2024-08-07 01:15:00 +02:00
self . PeerShareLinkCursor . execute (
2024-08-06 01:57:17 +02:00
"""
CREATE TABLE PeerShareLinks (
ShareID VARCHAR NOT NULL PRIMARY KEY , Configuration VARCHAR NOT NULL , Peer VARCHAR NOT NULL ,
ExpireDate DATETIME ,
SharedDate DATETIME DEFAULT ( datetime ( ' now ' , ' localtime ' ) )
)
2024-08-06 16:17:14 +02:00
"""
2024-08-06 01:57:17 +02:00
)
sqldb . commit ( )
2024-08-06 16:17:14 +02:00
self . __getSharedLinks ( )
# print(self.Links)
2024-08-06 01:57:17 +02:00
def __getSharedLinks ( self ) :
self . Links . clear ( )
2024-08-07 01:15:00 +02:00
allLinks = self . PeerShareLinkCursor . execute ( " SELECT * FROM PeerShareLinks WHERE ExpireDate IS NULL OR ExpireDate > datetime( ' now ' , ' localtime ' ) " ) . fetchall ( )
2024-08-06 01:57:17 +02:00
for link in allLinks :
2024-08-06 16:17:14 +02:00
self . Links . append ( PeerShareLink ( * link ) )
2024-08-06 01:57:17 +02:00
2024-08-06 16:17:14 +02:00
def getLink ( self , Configuration : str , Peer : str ) - > list [ PeerShareLink ] :
2024-08-07 01:15:00 +02:00
self . __getSharedLinks ( )
2024-08-06 01:57:17 +02:00
return list ( filter ( lambda x : x . Configuration == Configuration and x . Peer == Peer , self . Links ) )
2024-08-06 16:17:14 +02:00
def getLinkByID ( self , ShareID : str ) - > list [ PeerShareLink ] :
2024-08-07 01:15:00 +02:00
self . __getSharedLinks ( )
2024-08-06 01:57:17 +02:00
return list ( filter ( lambda x : x . ShareID == ShareID , self . Links ) )
2024-08-06 16:17:14 +02:00
def addLink ( self , Configuration : str , Peer : str , ExpireDate : datetime = None ) - > tuple [ bool , str ] :
2024-08-06 01:57:17 +02:00
try :
newShareID = str ( uuid . uuid4 ( ) )
if len ( self . getLink ( Configuration , Peer ) ) > 0 :
2024-08-07 01:15:00 +02:00
self . PeerShareLinkCursor . execute ( " UPDATE PeerShareLinks SET ExpireDate = datetime( ' now ' , ' localtime ' ) WHERE Configuration = ? AND Peer = ? " , ( Configuration , Peer , ) )
self . PeerShareLinkCursor . execute ( " INSERT INTO PeerShareLinks (ShareID, Configuration, Peer, ExpireDate) VALUES (?, ?, ?, ?) " , ( newShareID , Configuration , Peer , ExpireDate , ) )
2024-08-06 01:57:17 +02:00
sqldb . commit ( )
self . __getSharedLinks ( )
except Exception as e :
return False , str ( e )
2024-08-06 16:17:14 +02:00
return True , newShareID
2024-08-06 01:57:17 +02:00
2024-08-06 16:17:14 +02:00
def updateLinkExpireDate ( self , ShareID , ExpireDate : datetime = None ) - > tuple [ bool , str ] :
2024-08-07 01:15:00 +02:00
self . PeerShareLinkCursor . execute ( " UPDATE PeerShareLinks SET ExpireDate = ? WHERE ShareID = ?; " , ( ExpireDate , ShareID , ) )
sqldb . commit ( )
self . __getSharedLinks ( )
return True , " "
2024-08-06 01:57:17 +02:00
2024-06-17 21:16:42 +02:00
class WireguardConfiguration :
class InvalidConfigurationFileException ( Exception ) :
def __init__ ( self , m ) :
self . message = m
def __str__ ( self ) :
return self . message
def __init__ ( self , name : str = None , data : dict = None ) :
self . __parser : configparser . ConfigParser = configparser . ConfigParser ( strict = False )
self . __parser . optionxform = str
2024-08-16 00:26:20 +02:00
self . __configFileModifiedTime = None
2024-06-17 21:16:42 +02:00
self . Status : bool = False
self . Name : str = " "
self . PrivateKey : str = " "
self . PublicKey : str = " "
self . ListenPort : str = " "
self . Address : str = " "
self . DNS : str = " "
self . Table : str = " "
self . MTU : str = " "
self . PreUp : str = " "
self . PostUp : str = " "
self . PreDown : str = " "
self . PostDown : str = " "
self . SaveConfig : bool = True
if name is not None :
self . Name = name
self . __parser . read_file ( open ( os . path . join ( WG_CONF_PATH , f ' { self . Name } .conf ' ) ) )
sections = self . __parser . sections ( )
if " Interface " not in sections :
raise self . InvalidConfigurationFileException (
" [Interface] section not found in " + os . path . join ( WG_CONF_PATH , f ' { self . Name } .conf ' ) )
interfaceConfig = dict ( self . __parser . items ( " Interface " , True ) )
for i in dir ( self ) :
if str ( i ) in interfaceConfig . keys ( ) :
if isinstance ( getattr ( self , i ) , bool ) :
setattr ( self , i , _strToBool ( interfaceConfig [ i ] ) )
else :
setattr ( self , i , interfaceConfig [ i ] )
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
if self . PrivateKey :
self . PublicKey = self . __getPublicKey ( )
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
self . Status = self . getStatus ( )
2022-01-13 15:37:23 +01:00
2021-07-02 19:23:04 +02:00
else :
2024-06-17 21:16:42 +02:00
self . Name = data [ " ConfigurationName " ]
for i in dir ( self ) :
if str ( i ) in data . keys ( ) :
if isinstance ( getattr ( self , i ) , bool ) :
setattr ( self , i , _strToBool ( data [ i ] ) )
else :
setattr ( self , i , str ( data [ i ] ) )
# self.__createDatabase()
self . __parser [ " Interface " ] = {
" PrivateKey " : self . PrivateKey ,
" Address " : self . Address ,
" ListenPort " : self . ListenPort ,
" PreUp " : self . PreUp ,
" PreDown " : self . PreDown ,
" PostUp " : self . PostUp ,
" PostDown " : self . PostDown ,
" SaveConfig " : " true "
}
with open ( os . path . join ( DashboardConfig . GetConfig ( " Server " , " wg_conf_path " ) [ 1 ] ,
f " { self . Name } .conf " ) , " w+ " ) as configFile :
# print(self.__parser.sections())
self . __parser . write ( configFile )
self . Peers : list [ Peer ] = [ ]
# Create tables in database
self . __createDatabase ( )
self . getPeersList ( )
def __createDatabase ( self ) :
2024-08-09 23:29:57 +02:00
existingTables = sqldb . cursor ( ) . execute ( " SELECT name FROM sqlite_master WHERE type= ' table ' " ) . fetchall ( )
2024-06-17 21:16:42 +02:00
existingTables = [ t [ ' name ' ] for t in existingTables ]
if self . Name not in existingTables :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2022-01-13 01:53:36 +01:00
"""
2024-08-12 01:20:42 +02:00
CREATE TABLE ' %s ' (
2022-01-04 22:32:23 +01:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
2022-03-22 03:33:19 +01:00
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
2024-06-17 21:16:42 +02:00
""" % s elf.Name
)
sqldb . commit ( )
if f ' { self . Name } _restrict_access ' not in existingTables :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-06-17 21:16:42 +02:00
"""
2024-08-12 01:20:42 +02:00
CREATE TABLE ' %s _restrict_access ' (
2022-03-22 03:33:19 +01:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
2022-01-04 22:32:23 +01:00
PRIMARY KEY ( id )
)
2024-06-17 21:16:42 +02:00
""" % s elf.Name
)
sqldb . commit ( )
if f ' { self . Name } _transfer ' not in existingTables :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-06-17 21:16:42 +02:00
"""
2024-08-12 01:20:42 +02:00
CREATE TABLE ' %s _transfer ' (
2024-06-17 21:16:42 +02:00
id VARCHAR NOT NULL , total_receive FLOAT NULL ,
2022-04-06 03:39:47 +02:00
total_sent FLOAT NULL , total_data FLOAT NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , time DATETIME
)
2024-06-17 21:16:42 +02:00
""" % s elf.Name
)
sqldb . commit ( )
if f ' { self . Name } _deleted ' not in existingTables :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-06-17 21:16:42 +02:00
"""
2024-08-12 01:20:42 +02:00
CREATE TABLE ' %s _deleted ' (
2024-06-17 21:16:42 +02:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
""" % s elf.Name
)
sqldb . commit ( )
2024-08-06 01:57:17 +02:00
2024-06-17 21:16:42 +02:00
def __getPublicKey ( self ) - > str :
return _generatePublicKey ( self . PrivateKey ) [ 1 ]
def getStatus ( self ) - > bool :
self . Status = self . Name in psutil . net_if_addrs ( ) . keys ( )
return self . Status
def __getRestrictedPeers ( self ) :
self . RestrictedPeers = [ ]
2024-08-12 01:20:42 +02:00
restricted = sqldb . cursor ( ) . execute ( " SELECT * FROM ' %s _restrict_access ' " % self . Name ) . fetchall ( )
2024-06-17 21:16:42 +02:00
for i in restricted :
self . RestrictedPeers . append ( Peer ( i , self ) )
def __getPeers ( self ) :
2024-08-16 00:26:20 +02:00
mt = os . path . getmtime ( os . path . join ( WG_CONF_PATH , f ' { self . Name } .conf ' ) )
2024-08-18 02:19:52 +02:00
# if self.__configFileModifiedTime is None or self.__configFileModifiedTime != mt:
self . Peers = [ ]
with open ( os . path . join ( WG_CONF_PATH , f ' { self . Name } .conf ' ) , ' r ' ) as configFile :
p = [ ]
pCounter = - 1
content = configFile . read ( ) . split ( ' \n ' )
try :
peerStarts = content . index ( " [Peer] " )
content = content [ peerStarts : ]
for i in content :
if not regex_match ( " #(.*) " , i ) and not regex_match ( " ;(.*) " , i ) :
if i == " [Peer] " :
pCounter + = 1
p . append ( { } )
p [ pCounter ] [ " name " ] = " "
else :
if len ( i ) > 0 :
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
if len ( split ) == 2 :
p [ pCounter ] [ split [ 0 ] ] = split [ 1 ]
2024-08-15 23:45:54 +02:00
2024-08-18 02:19:52 +02:00
if regex_match ( " #Name# = (.*) " , i ) :
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
print ( split )
if len ( split ) == 2 :
p [ pCounter ] [ " name " ] = split [ 1 ]
for i in p :
if " PublicKey " in i . keys ( ) :
checkIfExist = sqldb . cursor ( ) . execute ( " SELECT * FROM ' %s ' WHERE id = ? " % self . Name ,
( ( i [ ' PublicKey ' ] ) , ) ) . fetchone ( )
if checkIfExist is None :
newPeer = {
" id " : i [ ' PublicKey ' ] ,
" private_key " : " " ,
" DNS " : DashboardConfig . GetConfig ( " Peers " , " peer_global_DNS " ) [ 1 ] ,
" endpoint_allowed_ip " : DashboardConfig . GetConfig ( " Peers " , " peer_endpoint_allowed_ip " ) [
1 ] ,
" name " : i . get ( " name " ) ,
" total_receive " : 0 ,
" total_sent " : 0 ,
" total_data " : 0 ,
" endpoint " : " N/A " ,
" status " : " stopped " ,
" latest_handshake " : " N/A " ,
" allowed_ip " : i . get ( " AllowedIPs " , " N/A " ) ,
" cumu_receive " : 0 ,
" cumu_sent " : 0 ,
" cumu_data " : 0 ,
" traffic " : [ ] ,
" mtu " : DashboardConfig . GetConfig ( " Peers " , " peer_mtu " ) [ 1 ] ,
" keepalive " : DashboardConfig . GetConfig ( " Peers " , " peer_keep_alive " ) [ 1 ] ,
" remote_endpoint " : DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] ,
" preshared_key " : i [ " PresharedKey " ] if " PresharedKey " in i . keys ( ) else " "
}
sqldb . cursor ( ) . execute (
"""
INSERT INTO ' %s '
VALUES ( : id , : private_key , : DNS , : endpoint_allowed_ip , : name , : total_receive , : total_sent ,
: total_data , : endpoint , : status , : latest_handshake , : allowed_ip , : cumu_receive , : cumu_sent ,
: cumu_data , : mtu , : keepalive , : remote_endpoint , : preshared_key ) ;
""" % s elf.Name
, newPeer )
sqldb . commit ( )
self . Peers . append ( Peer ( newPeer , self ) )
else :
sqldb . cursor ( ) . execute ( " UPDATE ' %s ' SET allowed_ip = ? WHERE id = ? " % self . Name ,
( i . get ( " AllowedIPs " , " N/A " ) , i [ ' PublicKey ' ] , ) )
sqldb . commit ( )
self . Peers . append ( Peer ( checkIfExist , self ) )
except Exception as e :
print ( f " [WGDashboard] { self . Name } Error: { str ( e ) } " )
2024-08-16 00:26:20 +02:00
self . __configFileModifiedTime = mt
2024-08-09 05:27:13 +02:00
def addPeers ( self , peers : list ) :
for p in peers :
subprocess . check_output ( f " wg set { self . Name } peer { p [ ' id ' ] } allowed-ips { p [ ' allowed_ip ' ] } " ,
shell = True , stderr = subprocess . STDOUT )
subprocess . check_output (
f " wg-quick save { self . Name } " , shell = True , stderr = subprocess . STDOUT )
self . getPeersList ( )
2024-06-17 21:16:42 +02:00
def searchPeer ( self , publicKey ) :
for i in self . Peers :
if i . id == publicKey :
return True , i
return False , None
def allowAccessPeers ( self , listOfPublicKeys ) :
# numOfAllowedPeers = 0
# numOfFailedToAllowPeers = 0
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
for i in listOfPublicKeys :
2024-08-12 01:20:42 +02:00
p = sqldb . cursor ( ) . execute ( " SELECT * FROM ' %s _restrict_access ' WHERE id = ? " % self . Name , ( i , ) ) . fetchone ( )
2024-06-17 21:16:42 +02:00
if p is not None :
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " INSERT INTO ' %s ' SELECT * FROM %s _restrict_access WHERE id = ? "
2024-06-17 21:16:42 +02:00
% ( self . Name , self . Name , ) , ( p [ ' id ' ] , ) )
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " DELETE FROM ' %s _restrict_access ' WHERE id = ? "
2024-06-17 21:16:42 +02:00
% self . Name , ( p [ ' id ' ] , ) )
subprocess . check_output ( f " wg set { self . Name } peer { p [ ' id ' ] } allowed-ips { p [ ' allowed_ip ' ] } " ,
shell = True , stderr = subprocess . STDOUT )
else :
return ResponseObject ( False , " Failed to allow access of peer " + i )
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
self . __getPeers ( )
return ResponseObject ( True , " Allow access successfully! " )
def restrictPeers ( self , listOfPublicKeys ) :
numOfRestrictedPeers = 0
numOfFailedToRestrictPeers = 0
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
for p in listOfPublicKeys :
found , pf = self . searchPeer ( p )
if found :
try :
subprocess . check_output ( f " wg set { self . Name } peer { pf . id } remove " ,
shell = True , stderr = subprocess . STDOUT )
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " INSERT INTO ' %s _restrict_access ' SELECT * FROM %s WHERE id = ? " %
2024-06-17 21:16:42 +02:00
( self . Name , self . Name , ) , ( pf . id , ) )
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " UPDATE ' %s _restrict_access ' SET status = ' stopped ' WHERE id = ? " %
2024-06-17 21:16:42 +02:00
( self . Name , ) , ( pf . id , ) )
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " DELETE FROM ' %s ' WHERE id = ? " % self . Name , ( pf . id , ) )
2024-06-17 21:16:42 +02:00
numOfRestrictedPeers + = 1
except Exception as e :
numOfFailedToRestrictPeers + = 1
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
self . __getPeers ( )
if numOfRestrictedPeers == len ( listOfPublicKeys ) :
return ResponseObject ( True , f " Restricted { numOfRestrictedPeers } peer(s) " )
return ResponseObject ( False ,
f " Restricted { numOfRestrictedPeers } peer(s) successfully. Failed to restrict { numOfFailedToRestrictPeers } peer(s) " )
pass
def deletePeers ( self , listOfPublicKeys ) :
numOfDeletedPeers = 0
numOfFailedToDeletePeers = 0
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
for p in listOfPublicKeys :
found , pf = self . searchPeer ( p )
if found :
try :
subprocess . check_output ( f " wg set { self . Name } peer { pf . id } remove " ,
shell = True , stderr = subprocess . STDOUT )
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " DELETE FROM ' %s ' WHERE id = ? " % self . Name , ( pf . id , ) )
2024-06-17 21:16:42 +02:00
numOfDeletedPeers + = 1
except Exception as e :
numOfFailedToDeletePeers + = 1
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
self . __getPeers ( )
if numOfDeletedPeers == len ( listOfPublicKeys ) :
return ResponseObject ( True , f " Deleted { numOfDeletedPeers } peer(s) " )
return ResponseObject ( False ,
f " Deleted { numOfDeletedPeers } peer(s) successfully. Failed to delete { numOfFailedToDeletePeers } peer(s) " )
def __savePeers ( self ) :
for i in self . Peers :
d = i . toJson ( )
sqldb . execute (
'''
2024-08-12 01:20:42 +02:00
UPDATE ' %s ' SET private_key = : private_key ,
2024-06-17 21:16:42 +02:00
DNS = : DNS , endpoint_allowed_ip = : endpoint_allowed_ip , name = : name ,
total_receive = : total_receive , total_sent = : total_sent , total_data = : total_data ,
endpoint = : endpoint , status = : status , latest_handshake = : latest_handshake ,
allowed_ip = : allowed_ip , cumu_receive = : cumu_receive , cumu_sent = : cumu_sent ,
cumu_data = : cumu_data , mtu = : mtu , keepalive = : keepalive ,
remote_endpoint = : remote_endpoint , preshared_key = : preshared_key WHERE id = : id
''' % s elf.Name, d
)
sqldb . commit ( )
def __wgSave ( self ) - > tuple [ bool , str ] | tuple [ bool , None ] :
try :
subprocess . check_output ( f " wg-quick save { self . Name } " , shell = True , stderr = subprocess . STDOUT )
return True , None
except subprocess . CalledProcessError as e :
return False , str ( e )
2021-08-15 05:30:05 +02:00
2024-06-17 21:16:42 +02:00
def getPeersLatestHandshake ( self ) :
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
try :
latestHandshake = subprocess . check_output ( f " wg show { self . Name } latest-handshakes " ,
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError :
return " stopped "
latestHandshake = latestHandshake . decode ( " UTF-8 " ) . split ( )
2022-03-29 21:11:50 +02:00
count = 0
2024-06-17 21:16:42 +02:00
now = datetime . now ( )
time_delta = timedelta ( minutes = 2 )
for _ in range ( int ( len ( latestHandshake ) / 2 ) ) :
minus = now - datetime . fromtimestamp ( int ( latestHandshake [ count + 1 ] ) )
if minus < time_delta :
status = " running "
else :
status = " stopped "
if int ( latestHandshake [ count + 1 ] ) > 0 :
2024-08-12 01:20:42 +02:00
sqldb . execute ( " UPDATE ' %s ' SET latest_handshake = ?, status = ? WHERE id= ? " % self . Name
2024-06-17 21:16:42 +02:00
, ( str ( minus ) . split ( " . " , maxsplit = 1 ) [ 0 ] , status , latestHandshake [ count ] , ) )
2022-03-29 21:11:50 +02:00
else :
2024-08-12 01:20:42 +02:00
sqldb . execute ( " UPDATE ' %s ' SET latest_handshake = ' No Handshake ' , status = ? WHERE id= ? " % self . Name
2024-06-17 21:16:42 +02:00
, ( status , latestHandshake [ count ] , ) )
sqldb . commit ( )
count + = 2
2024-08-16 00:26:20 +02:00
2024-06-17 21:16:42 +02:00
def getPeersTransfer ( self ) :
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
try :
data_usage = subprocess . check_output ( f " wg show { self . Name } transfer " ,
shell = True , stderr = subprocess . STDOUT )
data_usage = data_usage . decode ( " UTF-8 " ) . split ( " \n " )
data_usage = [ p . split ( " \t " ) for p in data_usage ]
for i in range ( len ( data_usage ) ) :
if len ( data_usage [ i ] ) == 3 :
2024-08-09 23:29:57 +02:00
cur_i = sqldb . cursor ( ) . execute (
2024-08-12 01:20:42 +02:00
" SELECT total_receive, total_sent, cumu_receive, cumu_sent, status FROM ' %s ' WHERE id= ? "
2024-06-17 21:16:42 +02:00
% self . Name , ( data_usage [ i ] [ 0 ] , ) ) . fetchone ( )
if cur_i is not None :
2024-08-16 00:26:20 +02:00
cur_i = dict ( cur_i )
2024-06-17 21:16:42 +02:00
total_sent = cur_i [ ' total_sent ' ]
total_receive = cur_i [ ' total_receive ' ]
2024-08-09 22:09:08 +02:00
cur_total_sent = float ( data_usage [ i ] [ 2 ] ) / ( 1024 * * 3 )
cur_total_receive = float ( data_usage [ i ] [ 1 ] ) / ( 1024 * * 3 )
2024-06-17 21:16:42 +02:00
cumulative_receive = cur_i [ ' cumu_receive ' ] + total_receive
cumulative_sent = cur_i [ ' cumu_sent ' ] + total_sent
if total_sent < = cur_total_sent and total_receive < = cur_total_receive :
total_sent = cur_total_sent
total_receive = cur_total_receive
else :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-08-12 01:20:42 +02:00
" UPDATE ' %s ' SET cumu_receive = ?, cumu_sent = ?, cumu_data = ? WHERE id = ? " %
2024-08-09 22:09:08 +02:00
self . Name , ( cumulative_receive , cumulative_sent ,
2024-08-10 04:16:38 +02:00
cumulative_sent + cumulative_receive ,
2024-06-17 21:16:42 +02:00
data_usage [ i ] [ 0 ] , ) )
2024-08-16 00:26:20 +02:00
sqldb . commit ( )
2024-06-17 21:16:42 +02:00
total_sent = 0
total_receive = 0
_ , p = self . searchPeer ( data_usage [ i ] [ 0 ] )
2024-08-09 22:09:08 +02:00
if p . total_receive != total_receive or p . total_sent != total_sent :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-08-12 01:20:42 +02:00
" UPDATE ' %s ' SET total_receive = ?, total_sent = ?, total_data = ? WHERE id = ? "
2024-08-09 22:09:08 +02:00
% self . Name , ( total_receive , total_sent ,
total_receive + total_sent , data_usage [ i ] [ 0 ] , ) )
2024-08-16 00:26:20 +02:00
sqldb . commit ( )
2024-06-17 21:16:42 +02:00
except Exception as e :
2024-08-16 00:26:20 +02:00
print ( f " [WGDashboard] { self . Name } Error: { str ( e ) } { str ( e . __traceback__ ) } " )
2024-06-17 21:16:42 +02:00
def getPeersEndpoint ( self ) :
2024-08-09 05:27:13 +02:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
try :
data_usage = subprocess . check_output ( f " wg show { self . Name } endpoints " ,
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError :
return " stopped "
data_usage = data_usage . decode ( " UTF-8 " ) . split ( )
count = 0
for _ in range ( int ( len ( data_usage ) / 2 ) ) :
2024-08-12 01:20:42 +02:00
sqldb . execute ( " UPDATE ' %s ' SET endpoint = ? WHERE id = ? " % self . Name
2024-06-17 21:16:42 +02:00
, ( data_usage [ count + 1 ] , data_usage [ count ] , ) )
sqldb . commit ( )
count + = 2
def toggleConfiguration ( self ) - > [ bool , str ] :
self . getStatus ( )
if self . Status :
2022-01-06 21:17:43 +01:00
try :
2024-06-17 21:16:42 +02:00
check = subprocess . check_output ( f " wg-quick down { self . Name } " ,
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError as exc :
return False , str ( exc . output . strip ( ) . decode ( " utf-8 " ) )
else :
try :
check = subprocess . check_output ( f " wg-quick up { self . Name } " ,
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError as exc :
return False , str ( exc . output . strip ( ) . decode ( " utf-8 " ) )
self . getStatus ( )
return True , None
def getPeersList ( self ) :
self . __getPeers ( )
return self . Peers
def getRestrictedPeersList ( self ) - > list :
self . __getRestrictedPeers ( )
return self . RestrictedPeers
def toJson ( self ) :
self . Status = self . getStatus ( )
return {
" Status " : self . Status ,
" Name " : self . Name ,
" PrivateKey " : self . PrivateKey ,
" PublicKey " : self . PublicKey ,
" Address " : self . Address ,
" ListenPort " : self . ListenPort ,
" PreUp " : self . PreUp ,
" PreDown " : self . PreDown ,
" PostUp " : self . PostUp ,
" PostDown " : self . PostDown ,
2024-08-09 22:09:55 +02:00
" SaveConfig " : self . SaveConfig ,
2024-08-09 22:09:23 +02:00
" DataUsage " : {
" Total " : sum ( list ( map ( lambda x : x . cumu_data + x . total_data , self . Peers ) ) ) ,
" Sent " : sum ( list ( map ( lambda x : x . cumu_sent + x . total_sent , self . Peers ) ) ) ,
" Receive " : sum ( list ( map ( lambda x : x . cumu_receive + x . total_receive , self . Peers ) ) )
} ,
2024-08-09 23:29:57 +02:00
" ConnectedPeers " : len ( list ( filter ( lambda x : x . status == " running " , self . Peers ) ) )
2024-06-17 21:16:42 +02:00
}
2022-01-06 21:17:43 +01:00
2024-06-17 21:16:42 +02:00
class Peer :
def __init__ ( self , tableData , configuration : WireguardConfiguration ) :
self . configuration = configuration
self . id = tableData [ " id " ]
self . private_key = tableData [ " private_key " ]
self . DNS = tableData [ " DNS " ]
self . endpoint_allowed_ip = tableData [ " endpoint_allowed_ip " ]
self . name = tableData [ " name " ]
self . total_receive = tableData [ " total_receive " ]
self . total_sent = tableData [ " total_sent " ]
self . total_data = tableData [ " total_data " ]
self . endpoint = tableData [ " endpoint " ]
self . status = tableData [ " status " ]
self . latest_handshake = tableData [ " latest_handshake " ]
self . allowed_ip = tableData [ " allowed_ip " ]
self . cumu_receive = tableData [ " cumu_receive " ]
self . cumu_sent = tableData [ " cumu_sent " ]
self . cumu_data = tableData [ " cumu_data " ]
self . mtu = tableData [ " mtu " ]
self . keepalive = tableData [ " keepalive " ]
self . remote_endpoint = tableData [ " remote_endpoint " ]
self . preshared_key = tableData [ " preshared_key " ]
self . jobs : list [ PeerJob ] = [ ]
2024-08-06 16:17:14 +02:00
self . ShareLink : list [ PeerShareLink ] = [ ]
2024-06-17 21:16:42 +02:00
self . getJobs ( )
2024-08-06 16:17:14 +02:00
self . getShareLink ( )
2024-06-17 21:16:42 +02:00
def toJson ( self ) :
2024-06-25 17:02:13 +02:00
self . getJobs ( )
2024-08-06 16:17:14 +02:00
self . getShareLink ( )
2024-06-17 21:16:42 +02:00
return self . __dict__
def __repr__ ( self ) :
return str ( self . toJson ( ) )
def updatePeer ( self , name : str , private_key : str ,
preshared_key : str ,
dns_addresses : str , allowed_ip : str , endpoint_allowed_ip : str , mtu : int ,
keepalive : int ) - > ResponseObject :
2024-08-09 05:27:13 +02:00
if not self . configuration . getStatus ( ) :
self . configuration . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
existingAllowedIps = [ item for row in list (
map ( lambda x : [ q . strip ( ) for q in x . split ( ' , ' ) ] ,
map ( lambda y : y . allowed_ip ,
list ( filter ( lambda k : k . id != self . id , self . configuration . getPeersList ( ) ) ) ) ) ) for item in row ]
if allowed_ip in existingAllowedIps :
return ResponseObject ( False , " Allowed IP already taken by another peer. " )
if not _checkIPWithRange ( endpoint_allowed_ip ) :
return ResponseObject ( False , f " Endpoint Allowed IPs format is incorrect. " )
if len ( dns_addresses ) > 0 and not _checkDNS ( dns_addresses ) :
return ResponseObject ( False , f " DNS format is incorrect. " )
if mtu < 0 or mtu > 1460 :
return ResponseObject ( False , " MTU format is not correct. " )
if keepalive < 0 :
return ResponseObject ( False , " Persistent Keepalive format is not correct. " )
if len ( private_key ) > 0 :
pubKey = _generatePublicKey ( private_key )
if not pubKey [ 0 ] or pubKey [ 1 ] != self . id :
return ResponseObject ( False , " Private key does not match with the public key. " )
try :
if len ( preshared_key ) > 0 :
rd = random . Random ( )
uid = uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 )
with open ( f " { uid } " , " w+ " ) as f :
f . write ( preshared_key )
updatePsk = subprocess . check_output (
f " wg set { self . configuration . Name } peer { self . id } preshared-key { uid } " ,
shell = True , stderr = subprocess . STDOUT )
os . remove ( str ( uid ) )
if len ( updatePsk . decode ( ) . strip ( " \n " ) ) != 0 :
return ResponseObject ( False ,
" Update peer failed when updating preshared key " )
updateAllowedIp = subprocess . check_output (
f ' wg set { self . configuration . Name } peer { self . id } allowed-ips " { allowed_ip . replace ( " " , " " ) } " ' ,
shell = True , stderr = subprocess . STDOUT )
if len ( updateAllowedIp . decode ( ) . strip ( " \n " ) ) != 0 :
return ResponseObject ( False ,
" Update peer failed when updating allowed IPs " )
saveConfig = subprocess . check_output ( f " wg-quick save { self . configuration . Name } " ,
shell = True , stderr = subprocess . STDOUT )
if f " wg showconf { self . configuration . Name } " not in saveConfig . decode ( ) . strip ( ' \n ' ) :
return ResponseObject ( False ,
" Update peer failed when saving the configuration. " )
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute (
2024-08-12 01:20:42 +02:00
''' UPDATE ' %s ' SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ?, mtu = ?,
2024-06-17 21:16:42 +02:00
keepalive = ? , preshared_key = ? WHERE id = ? ''' % s elf.configuration.Name,
( name , private_key , dns_addresses , endpoint_allowed_ip , mtu ,
keepalive , preshared_key , self . id , )
)
2024-08-09 05:27:13 +02:00
sqldb . commit ( )
2024-06-17 21:16:42 +02:00
return ResponseObject ( )
except subprocess . CalledProcessError as exc :
return ResponseObject ( False , exc . output . decode ( " UTF-8 " ) . strip ( ) )
2021-08-14 23:13:16 +02:00
2024-06-17 21:16:42 +02:00
def downloadPeer ( self ) - > dict [ str , str ] :
filename = self . name
if len ( filename ) == 0 :
filename = " UntitledPeer "
filename = " " . join ( filename . split ( ' ' ) )
filename = f " { filename } _ { self . configuration . Name } "
illegal_filename = [ " . " , " , " , " / " , " ? " , " < " , " > " , " \\ " , " : " , " * " , ' | ' ' \" ' , " com1 " , " com2 " , " com3 " ,
" com4 " , " com5 " , " com6 " , " com7 " , " com8 " , " com9 " , " lpt1 " , " lpt2 " , " lpt3 " , " lpt4 " ,
" lpt5 " , " lpt6 " , " lpt7 " , " lpt8 " , " lpt9 " , " con " , " nul " , " prn " ]
for i in illegal_filename :
filename = filename . replace ( i , " " )
peerConfiguration = f ''' [Interface]
PrivateKey = { self . private_key }
Address = { self . allowed_ip }
MTU = { str ( self . mtu ) }
'''
if len ( self . DNS ) > 0 :
peerConfiguration + = f " DNS = { self . DNS } \n "
peerConfiguration + = f '''
[ Peer ]
PublicKey = { self . configuration . PublicKey }
AllowedIPs = { self . endpoint_allowed_ip }
Endpoint = { DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] } : { self . configuration . ListenPort }
PersistentKeepalive = { str ( self . keepalive ) }
'''
if len ( self . preshared_key ) > 0 :
peerConfiguration + = f " PresharedKey = { self . preshared_key } \n "
return {
" fileName " : filename ,
" file " : peerConfiguration
}
2022-01-02 20:44:27 +01:00
2024-06-17 21:16:42 +02:00
def getJobs ( self ) :
self . jobs = AllPeerJobs . searchJob ( self . configuration . Name , self . id )
2024-08-06 16:17:14 +02:00
def getShareLink ( self ) :
self . ShareLink = AllPeerShareLinks . getLink ( self . configuration . Name , self . id )
2024-08-09 05:27:13 +02:00
def resetDataUsage ( self , type ) :
try :
if type == " total " :
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " UPDATE ' %s ' SET total_data = 0, cumu_data = 0, total_receive = 0, cumu_receive = 0, total_sent = 0, cumu_sent = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2024-08-09 05:27:13 +02:00
elif type == " receive " :
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " UPDATE ' %s ' SET total_receive = 0, cumu_receive = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2024-08-09 05:27:13 +02:00
elif type == " sent " :
2024-08-12 01:20:42 +02:00
sqldb . cursor ( ) . execute ( " UPDATE ' %s ' SET total_sent = 0, cumu_sent = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2024-08-09 05:27:13 +02:00
else :
return False
except Exception as e :
return False
return True
2024-08-14 07:17:47 +02:00
2024-06-17 21:16:42 +02:00
# Regex Match
def regex_match ( regex , text ) :
pattern = re . compile ( regex )
return pattern . search ( text ) is not None
def iPv46RegexCheck ( ip ) :
return re . match (
' ((^ \ s*((([0-9]|[1-9][0-9]|1[0-9] {2} |2[0-4][0-9]|25[0-5]) \ .) {3} ([0-9]|[1-9][0-9]|1[0-9] {2} |2[0-4][0-9]|25[0-5])) \ s*$)|(^ \ s*((([0-9a-f] { 1,4}:) {7} ([0-9a-f] { 1,4}|:))|(([0-9a-f] { 1,4}:) {6} (:[0-9a-f] { 1,4}|((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} )|:))|(([0-9a-f] { 1,4}:) {5} (((:[0-9a-f] { 1,4}) { 1,2})|:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} )|:))|(([0-9a-f] { 1,4}:) {4} (((:[0-9a-f] { 1,4}) { 1,3})|((:[0-9a-f] { 1,4})?:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} ))|:))|(([0-9a-f] { 1,4}:) {3} (((:[0-9a-f] { 1,4}) { 1,4})|((:[0-9a-f] { 1,4}) { 0,2}:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} ))|:))|(([0-9a-f] { 1,4}:) {2} (((:[0-9a-f] { 1,4}) { 1,5})|((:[0-9a-f] { 1,4}) { 0,3}:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} ))|:))|(([0-9a-f] { 1,4}:) {1} (((:[0-9a-f] { 1,4}) { 1,6})|((:[0-9a-f] { 1,4}) { 0,4}:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} ))|:))|(:(((:[0-9a-f] { 1,4}) { 1,7})|((:[0-9a-f] { 1,4}) { 0,5}:((25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)( \ .(25[0-5]|2[0-4] \ d|1 \ d \ d|[1-9]? \ d)) {3} ))|:)))( % .+)? \ s*$)) ' ,
ip )
2024-07-31 00:45:05 +02:00
class DashboardAPIKey :
def __init__ ( self , Key : str , CreatedAt : str , ExpiredAt : str ) :
self . Key = Key
self . CreatedAt = CreatedAt
self . ExpiredAt = ExpiredAt
def toJson ( self ) :
return self . __dict__
2024-06-17 21:16:42 +02:00
class DashboardConfig :
def __init__ ( self ) :
if not os . path . exists ( DASHBOARD_CONF ) :
open ( DASHBOARD_CONF , " x " )
self . __config = configparser . ConfigParser ( strict = False )
self . __config . read_file ( open ( DASHBOARD_CONF , " r+ " ) )
self . hiddenAttribute = [ " totp_key " ]
self . __default = {
" Account " : {
" username " : " admin " ,
" password " : " admin " ,
" enable_totp " : " false " ,
2024-08-05 21:39:11 +02:00
" totp_verified " : " false " ,
2024-06-17 21:16:42 +02:00
" totp_key " : pyotp . random_base32 ( )
} ,
" Server " : {
" wg_conf_path " : " /etc/wireguard " ,
2024-08-14 07:17:47 +02:00
" app_prefix " : " " ,
2024-06-17 21:16:42 +02:00
" app_ip " : " 0.0.0.0 " ,
" app_port " : " 10086 " ,
" auth_req " : " true " ,
" version " : DASHBOARD_VERSION ,
" dashboard_refresh_interval " : " 60000 " ,
" dashboard_sort " : " status " ,
2024-07-31 00:45:05 +02:00
" dashboard_theme " : " dark " ,
" dashboard_api_key " : " false "
2024-06-17 21:16:42 +02:00
} ,
" Peers " : {
" peer_global_DNS " : " 1.1.1.1 " ,
" peer_endpoint_allowed_ip " : " 0.0.0.0/0 " ,
" peer_display_mode " : " grid " ,
" remote_endpoint " : ifcfg . default_interface ( ) [ ' inet ' ] ,
" peer_MTU " : " 1420 " ,
" peer_keep_alive " : " 21 "
} ,
" Other " : {
" welcome_session " : " true "
2024-08-15 22:55:34 +02:00
} ,
" Database " : {
" type " : " sqlite "
2024-06-17 21:16:42 +02:00
}
}
2022-01-02 20:44:27 +01:00
2024-06-17 21:16:42 +02:00
for section , keys in self . __default . items ( ) :
for key , value in keys . items ( ) :
exist , currentData = self . GetConfig ( section , key )
if not exist :
self . SetConfig ( section , key , value , True )
2024-07-31 00:45:05 +02:00
self . __createAPIKeyTable ( )
self . DashboardAPIKeys = self . __getAPIKeys ( )
2024-08-11 07:48:13 +02:00
self . APIAccessed = False
2024-07-31 00:45:05 +02:00
def __createAPIKeyTable ( self ) :
2024-08-09 23:29:57 +02:00
existingTable = sqldb . cursor ( ) . execute ( " SELECT name FROM sqlite_master WHERE type= ' table ' AND name = ' DashboardAPIKeys ' " ) . fetchall ( )
2024-07-31 00:45:05 +02:00
if len ( existingTable ) == 0 :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute ( " CREATE TABLE DashboardAPIKeys (Key VARCHAR NOT NULL PRIMARY KEY, CreatedAt DATETIME NOT NULL DEFAULT (datetime( ' now ' , ' localtime ' )), ExpiredAt VARCHAR) " )
2024-07-31 00:45:05 +02:00
sqldb . commit ( )
def __getAPIKeys ( self ) - > list [ DashboardAPIKey ] :
2024-08-09 23:29:57 +02:00
keys = sqldb . cursor ( ) . execute ( " SELECT * FROM DashboardAPIKeys WHERE ExpiredAt IS NULL OR ExpiredAt > datetime( ' now ' , ' localtime ' ) ORDER BY CreatedAt DESC " ) . fetchall ( )
2024-07-31 00:45:05 +02:00
fKeys = [ ]
for k in keys :
fKeys . append ( DashboardAPIKey ( * k ) )
return fKeys
def createAPIKeys ( self , ExpiredAt = None ) :
newKey = secrets . token_urlsafe ( 32 )
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute ( ' INSERT INTO DashboardAPIKeys (Key, ExpiredAt) VALUES (?, ?) ' , ( newKey , ExpiredAt , ) )
2024-07-31 00:45:05 +02:00
sqldb . commit ( )
self . DashboardAPIKeys = self . __getAPIKeys ( )
def deleteAPIKey ( self , key ) :
2024-08-09 23:29:57 +02:00
sqldb . cursor ( ) . execute ( " UPDATE DashboardAPIKeys SET ExpiredAt = datetime( ' now ' , ' localtime ' ) WHERE Key = ? " , ( key , ) )
2024-07-31 00:45:05 +02:00
sqldb . commit ( )
self . DashboardAPIKeys = self . __getAPIKeys ( )
2024-06-17 21:16:42 +02:00
def __configValidation ( self , key , value : Any ) - > [ bool , str ] :
if type ( value ) is str and len ( value ) == 0 :
return False , " Field cannot be empty! "
if key == " peer_global_dns " :
value = value . split ( " , " )
for i in value :
try :
ipaddress . ip_address ( i )
except ValueError as e :
return False , str ( e )
if key == " peer_endpoint_allowed_ip " :
value = value . split ( " , " )
for i in value :
try :
ipaddress . ip_network ( i , strict = False )
except Exception as e :
return False , str ( e )
if key == " wg_conf_path " :
if not os . path . exists ( value ) :
return False , f " { value } is not a valid path "
if key == " password " :
if self . GetConfig ( " Account " , " password " ) [ 0 ] :
if not self . __checkPassword (
value [ " currentPassword " ] , self . GetConfig ( " Account " , " password " ) [ 1 ] . encode ( " utf-8 " ) ) :
return False , " Current password does not match. "
if value [ " newPassword " ] != value [ " repeatNewPassword " ] :
return False , " New passwords does not match "
return True , " "
def generatePassword ( self , plainTextPassword : str ) :
2024-08-02 23:27:28 +02:00
return bcrypt . hashpw ( plainTextPassword . encode ( " utf-8 " ) , bcrypt . gensalt ( ) )
2024-06-17 21:16:42 +02:00
def __checkPassword ( self , plainTextPassword : str , hashedPassword : bytes ) :
return bcrypt . checkpw ( plainTextPassword . encode ( " utf-8 " ) , hashedPassword )
def SetConfig ( self , section : str , key : str , value : any , init : bool = False ) - > [ bool , str ] :
if key in self . hiddenAttribute and not init :
return False , None
if not init :
valid , msg = self . __configValidation ( key , value )
if not valid :
return False , msg
if section == " Account " and key == " password " :
if not init :
value = self . generatePassword ( value [ " newPassword " ] ) . decode ( " utf-8 " )
2021-08-06 05:15:50 +02:00
else :
2024-06-17 21:16:42 +02:00
value = self . generatePassword ( value ) . decode ( " utf-8 " )
2022-03-22 21:13:57 +01:00
2024-06-17 21:16:42 +02:00
if section not in self . __config :
self . __config [ section ] = { }
2021-12-28 20:53:51 +01:00
2024-06-17 21:16:42 +02:00
if key not in self . __config [ section ] . keys ( ) or value != self . __config [ section ] [ key ] :
if type ( value ) is bool :
if value :
self . __config [ section ] [ key ] = " true "
else :
self . __config [ section ] [ key ] = " false "
else :
self . __config [ section ] [ key ] = value
return self . SaveConfig ( ) , " "
return True , " "
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
def SaveConfig ( self ) - > bool :
try :
with open ( DASHBOARD_CONF , " w+ " , encoding = ' utf-8 ' ) as configFile :
self . __config . write ( configFile )
return True
except Exception as e :
return False
def GetConfig ( self , section , key ) - > [ any , bool ] :
if section not in self . __config :
return False , None
if key not in self . __config [ section ] :
return False , None
if self . __config [ section ] [ key ] in [ " 1 " , " yes " , " true " , " on " ] :
return True , True
if self . __config [ section ] [ key ] in [ " 0 " , " no " , " false " , " off " ] :
return True , False
return True , self . __config [ section ] [ key ]
def toJson ( self ) - > dict [ str , dict [ Any , Any ] ] :
the_dict = { }
for section in self . __config . sections ( ) :
the_dict [ section ] = { }
for key , val in self . __config . items ( section ) :
if key not in self . hiddenAttribute :
if val in [ " 1 " , " yes " , " true " , " on " ] :
the_dict [ section ] [ key ] = True
elif val in [ " 0 " , " no " , " false " , " off " ] :
the_dict [ section ] [ key ] = False
else :
the_dict [ section ] [ key ] = val
return the_dict
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
'''
Private Functions
'''
2021-05-04 07:32:34 +02:00
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
def _strToBool ( value : str ) - > bool :
return value . lower ( ) in ( " yes " , " true " , " t " , " 1 " , 1 )
2021-05-04 07:32:34 +02:00
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
def _regexMatch ( regex , text ) :
pattern = re . compile ( regex )
return pattern . search ( text ) is not None
2023-11-30 15:42:02 +01:00
2024-06-17 21:16:42 +02:00
def _getConfigurationList ( ) - > [ WireguardConfiguration ] :
configurations = { }
for i in os . listdir ( WG_CONF_PATH ) :
if _regexMatch ( " ^(. { 1,}).(conf)$ " , i ) :
i = i . replace ( ' .conf ' , ' ' )
try :
configurations [ i ] = WireguardConfiguration ( i )
except WireguardConfiguration . InvalidConfigurationFileException as e :
print ( f " { i } have an invalid configuration file. " )
return configurations
def _checkIPWithRange ( ip ) :
ip_patterns = (
r " ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)( \ .| \ /)) {4} ([0-9] { 1,2})(,|$) " ,
r " [0-9a-fA-F] { 0,4}(:([0-9a-fA-F] { 0,4})) { 1,7} \ /([0-9] { 1,3})(,|$) "
)
for match_pattern in ip_patterns :
match_result = regex_match ( match_pattern , ip )
if match_result :
result = match_result
break
else :
result = None
2022-01-18 16:42:23 +01:00
2024-06-17 21:16:42 +02:00
return result
2022-01-18 16:42:23 +01:00
2024-01-09 06:25:47 +01:00
2024-06-17 21:16:42 +02:00
def _checkIP ( ip ) :
ip_patterns = (
r " ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)( \ .|$)) {4} " ,
r " [0-9a-fA-F] { 0,4}(:([0-9a-fA-F] { 0,4})) { 1,7}$ "
)
for match_pattern in ip_patterns :
match_result = regex_match ( match_pattern , ip )
if match_result :
result = match_result
break
else :
result = None
2021-09-08 18:39:25 +02:00
2024-06-17 21:16:42 +02:00
return result
2021-12-26 00:26:39 +01:00
2021-05-04 07:32:34 +02:00
2024-06-17 21:16:42 +02:00
def _checkDNS ( dns ) :
dns = dns . replace ( ' ' , ' ' ) . split ( ' , ' )
for i in dns :
if not ( _checkIP ( i ) or regex_match ( r " (?:[a-z0-9](?:[a-z0-9-] { 0,61}[a-z0-9])? \ .)+[a-z][a-z] { 0,61}[a-z] " , i ) ) :
return False
return True
2021-12-26 00:26:39 +01:00
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
def _generatePublicKey ( privateKey ) - > tuple [ bool , str ] | tuple [ bool , None ] :
2021-05-04 07:32:34 +02:00
try :
2024-06-17 21:16:42 +02:00
publicKey = subprocess . check_output ( f " wg pubkey " , input = privateKey . encode ( ) , shell = True ,
stderr = subprocess . STDOUT )
return True , publicKey . decode ( ) . strip ( ' \n ' )
except subprocess . CalledProcessError :
return False , None
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
def _generatePrivateKey ( ) - > [ bool , str ] :
try :
publicKey = subprocess . check_output ( f " wg genkey " , shell = True ,
stderr = subprocess . STDOUT )
return True , publicKey . decode ( ) . strip ( ' \n ' )
except subprocess . CalledProcessError :
return False , None
def _getWireguardConfigurationAvailableIP ( configName : str ) - > tuple [ bool , list [ str ] ] | tuple [ bool , None ] :
if configName not in WireguardConfigurations . keys ( ) :
return False , None
configuration = WireguardConfigurations [ configName ]
if len ( configuration . Address ) > 0 :
address = configuration . Address . split ( ' , ' )
existedAddress = [ ]
availableAddress = [ ]
for p in configuration . Peers :
if len ( p . allowed_ip ) > 0 :
add = p . allowed_ip . split ( ' , ' )
for i in add :
a , c = i . split ( ' / ' )
existedAddress . append ( ipaddress . ip_address ( a . replace ( " " , " " ) ) )
for i in address :
addressSplit , cidr = i . split ( ' / ' )
existedAddress . append ( ipaddress . ip_address ( addressSplit . replace ( " " , " " ) ) )
for i in address :
network = ipaddress . ip_network ( i . replace ( " " , " " ) , False )
count = 0
for h in network . hosts ( ) :
if h not in existedAddress :
availableAddress . append ( ipaddress . ip_network ( h ) . compressed )
count + = 1
if network . version == 6 and count > 255 :
break
return True , availableAddress
2021-05-04 07:32:34 +02:00
2024-06-17 21:16:42 +02:00
return False , None
2021-12-26 00:26:39 +01:00
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
sqldb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard.db ' ) , check_same_thread = False )
sqldb . row_factory = sqlite3 . Row
cursor = sqldb . cursor ( )
DashboardConfig = DashboardConfig ( )
_ , APP_PREFIX = DashboardConfig . GetConfig ( " Server " , " app_prefix " )
cors = CORS ( app , resources = { rf " { APP_PREFIX } /api/* " : {
" origins " : " * " ,
" methods " : " DELETE, POST, GET, OPTIONS " ,
" allow_headers " : [ " Content-Type " , " wg-dashboard-apikey " ]
} } )
2024-08-11 07:48:13 +02:00
2024-06-17 21:16:42 +02:00
'''
API Routes
'''
2021-05-04 07:32:34 +02:00
2024-08-14 07:17:47 +02:00
2024-06-17 21:16:42 +02:00
@app.before_request
def auth_req ( ) :
2024-08-11 01:03:21 +02:00
if request . method . lower ( ) == ' options ' :
return ResponseObject ( True )
2024-08-11 07:48:13 +02:00
DashboardConfig . APIAccessed = False
2024-08-03 23:03:39 +02:00
if " api " in request . path :
if str ( request . method ) == " GET " :
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = str ( request . args ) )
elif str ( request . method ) == " POST " :
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " Request Args: { str ( request . args ) } Body: { str ( request . get_json ( ) ) } " )
2024-06-17 21:16:42 +02:00
authenticationRequired = DashboardConfig . GetConfig ( " Server " , " auth_req " ) [ 1 ]
2024-08-02 23:27:28 +02:00
d = request . headers
2024-06-17 21:16:42 +02:00
if authenticationRequired :
2024-08-02 23:27:28 +02:00
apiKey = d . get ( ' wg-dashboard-apikey ' )
2024-07-31 08:27:44 +02:00
apiKeyEnabled = DashboardConfig . GetConfig ( " Server " , " dashboard_api_key " ) [ 1 ]
if apiKey is not None and len ( apiKey ) > 0 and apiKeyEnabled :
apiKeyExist = len ( list ( filter ( lambda x : x . Key == apiKey , DashboardConfig . DashboardAPIKeys ) ) ) == 1
2024-08-03 23:03:39 +02:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " API Key Access: { ( ' true ' if apiKeyExist else ' false ' ) } - Key: { apiKey } " )
2024-07-31 08:27:44 +02:00
if not apiKeyExist :
2024-08-11 07:48:13 +02:00
DashboardConfig . APIAccessed = False
2024-07-31 08:27:44 +02:00
response = Flask . make_response ( app , {
" status " : False ,
" message " : " API Key does not exist " ,
" data " : None
} )
response . content_type = " application/json "
response . status_code = 401
return response
2024-08-11 07:48:13 +02:00
DashboardConfig . APIAccessed = True
2024-07-31 08:27:44 +02:00
else :
2024-08-11 07:48:13 +02:00
DashboardConfig . APIAccessed = False
2024-08-14 07:17:47 +02:00
if ( ' /static/ ' not in request . path and " username " not in session
and ( f " { ( APP_PREFIX if len ( APP_PREFIX ) > 0 else ' ' ) } / " != request . path
and f " { ( APP_PREFIX if len ( APP_PREFIX ) > 0 else ' ' ) } " != request . path )
2024-07-31 08:27:44 +02:00
and " validateAuthentication " not in request . path and " authenticate " not in request . path
and " getDashboardConfiguration " not in request . path and " getDashboardTheme " not in request . path
2024-08-07 06:37:05 +02:00
and " sharePeer/get " not in request . path
2024-07-31 08:27:44 +02:00
and " isTotpEnabled " not in request . path
) :
response = Flask . make_response ( app , {
" status " : False ,
" message " : " Unauthorized access. " ,
" data " : None
} )
response . content_type = " application/json "
response . status_code = 401
return response
2021-05-05 03:26:40 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/handshake ' , methods = [ " GET " , " OPTIONS " ] )
2024-08-11 01:03:21 +02:00
def API_ValidateAPIKey ( ) :
return ResponseObject ( True )
2021-12-26 00:26:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/validateAuthentication ' , methods = [ " GET " ] )
2024-06-17 21:16:42 +02:00
def API_ValidateAuthentication ( ) :
token = request . cookies . get ( " authToken " ) + " "
if token == " " or " username " not in session or session [ " username " ] != token :
2024-08-15 22:55:34 +02:00
return ResponseObject ( False , " Invalid authentication. " )
2024-06-17 21:16:42 +02:00
return ResponseObject ( True )
2021-08-14 23:13:16 +02:00
2021-12-26 00:26:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/authenticate ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_AuthenticateLogin ( ) :
data = request . get_json ( )
2024-08-11 07:48:13 +02:00
if DashboardConfig . APIAccessed :
authToken = hashlib . sha256 ( f " { request . headers . get ( ' wg-dashboard-apikey ' ) } { datetime . now ( ) } " . encode ( ) ) . hexdigest ( )
session [ ' username ' ] = authToken
resp = ResponseObject ( True , DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] )
2024-08-15 22:55:34 +02:00
resp . set_cookie ( " authToken " , authToken )
2024-08-11 07:48:13 +02:00
session . permanent = True
return resp
2024-06-17 21:16:42 +02:00
valid = bcrypt . checkpw ( data [ ' password ' ] . encode ( " utf-8 " ) ,
DashboardConfig . GetConfig ( " Account " , " password " ) [ 1 ] . encode ( " utf-8 " ) )
totpEnabled = DashboardConfig . GetConfig ( " Account " , " enable_totp " ) [ 1 ]
totpValid = False
if totpEnabled :
totpValid = pyotp . TOTP ( DashboardConfig . GetConfig ( " Account " , " totp_key " ) [ 1 ] ) . now ( ) == data [ ' totp ' ]
if ( valid
and data [ ' username ' ] == DashboardConfig . GetConfig ( " Account " , " username " ) [ 1 ]
and ( ( totpEnabled and totpValid ) or not totpEnabled )
) :
authToken = hashlib . sha256 ( f " { data [ ' username ' ] } { datetime . now ( ) } " . encode ( ) ) . hexdigest ( )
session [ ' username ' ] = authToken
resp = ResponseObject ( True , DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] )
resp . set_cookie ( " authToken " , authToken )
session . permanent = True
2024-08-03 23:03:39 +02:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " Login success: { data [ ' username ' ] } " )
2024-06-17 21:16:42 +02:00
return resp
2024-08-03 23:03:39 +02:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " Login failed: { data [ ' username ' ] } " )
2024-06-17 21:16:42 +02:00
if totpEnabled :
return ResponseObject ( False , " Sorry, your username, password or OTP is incorrect. " )
2021-12-29 18:17:44 +01:00
else :
2024-06-17 21:16:42 +02:00
return ResponseObject ( False , " Sorry, your username or password is incorrect. " )
2021-05-14 00:00:40 +02:00
2021-12-26 00:26:39 +01:00
2024-08-15 22:55:34 +02:00
@app.get ( f ' { APP_PREFIX } /api/signout ' )
2024-06-17 21:16:42 +02:00
def API_SignOut ( ) :
resp = ResponseObject ( True , " " )
resp . delete_cookie ( " authToken " )
return resp
2022-01-02 14:35:39 +01:00
2020-10-18 18:23:38 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getWireguardConfigurations ' , methods = [ " GET " ] )
2024-06-17 21:16:42 +02:00
def API_getWireguardConfigurations ( ) :
2024-08-09 23:29:57 +02:00
# WireguardConfigurations = _getConfigurationList()
2024-06-17 21:16:42 +02:00
return ResponseObject ( data = [ wc for wc in WireguardConfigurations . values ( ) ] )
2022-01-02 20:44:27 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/addWireguardConfiguration ' , methods = [ " POST " ] )
2024-06-17 21:16:42 +02:00
def API_addWireguardConfiguration ( ) :
2022-01-06 21:17:43 +01:00
data = request . get_json ( )
2024-06-17 21:16:42 +02:00
keys = [
" ConfigurationName " ,
" Address " ,
" ListenPort " ,
" PrivateKey " ,
" PublicKey " ,
" PresharedKey " ,
" PreUp " ,
" PreDown " ,
" PostUp " ,
" PostDown " ,
]
requiredKeys = [
" ConfigurationName " , " Address " , " ListenPort " , " PrivateKey "
]
for i in keys :
if i not in data . keys ( ) or ( i in requiredKeys and len ( str ( data [ i ] ) ) == 0 ) :
return ResponseObject ( False , " Please provide all required parameters. " )
# Check duplicate names, ports, address
for i in WireguardConfigurations . values ( ) :
if i . Name == data [ ' ConfigurationName ' ] :
return ResponseObject ( False ,
f " Already have a configuration with the name \" { data [ ' ConfigurationName ' ] } \" " ,
" ConfigurationName " )
if str ( i . ListenPort ) == str ( data [ " ListenPort " ] ) :
return ResponseObject ( False ,
f " Already have a configuration with the port \" { data [ ' ListenPort ' ] } \" " ,
" ListenPort " )
if i . Address == data [ " Address " ] :
return ResponseObject ( False ,
f " Already have a configuration with the address \" { data [ ' Address ' ] } \" " ,
" Address " )
WireguardConfigurations [ data [ ' ConfigurationName ' ] ] = WireguardConfiguration ( data = data )
return ResponseObject ( )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/toggleWireguardConfiguration/ ' )
2024-06-17 21:16:42 +02:00
def API_toggleWireguardConfiguration ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or len (
configurationName ) == 0 or configurationName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please provide a valid configuration name " )
toggleStatus , msg = WireguardConfigurations [ configurationName ] . toggleConfiguration ( )
return ResponseObject ( toggleStatus , msg , WireguardConfigurations [ configurationName ] . Status )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getDashboardConfiguration ' , methods = [ " GET " ] )
2024-06-17 21:16:42 +02:00
def API_getDashboardConfiguration ( ) :
return ResponseObject ( data = DashboardConfig . toJson ( ) )
2024-08-16 05:20:29 +02:00
# @app.route(f'{APP_PREFIX}/api/updateDashboardConfiguration', methods=["POST"])
# def API_updateDashboardConfiguration():
# data = request.get_json()
# for section in data['DashboardConfiguration'].keys():
# for key in data['DashboardConfiguration'][section].keys():
# if not DashboardConfig.SetConfig(section, key, data['DashboardConfiguration'][section][key])[0]:
# return ResponseObject(False, "Section or value is invalid.")
# return ResponseObject()
2024-06-17 21:16:42 +02:00
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/updateDashboardConfigurationItem ' , methods = [ " POST " ] )
2024-06-17 21:16:42 +02:00
def API_updateDashboardConfigurationItem ( ) :
2020-12-27 05:42:41 +01:00
data = request . get_json ( )
2024-06-17 21:16:42 +02:00
if " section " not in data . keys ( ) or " key " not in data . keys ( ) or " value " not in data . keys ( ) :
return ResponseObject ( False , " Invalid request. " )
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
valid , msg = DashboardConfig . SetConfig (
data [ " section " ] , data [ " key " ] , data [ ' value ' ] )
2022-01-02 20:44:27 +01:00
2024-06-17 21:16:42 +02:00
if not valid :
return ResponseObject ( False , msg )
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
return ResponseObject ( )
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getDashboardAPIKeys ' , methods = [ ' GET ' ] )
2024-07-31 00:45:05 +02:00
def API_getDashboardAPIKeys ( ) :
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
return ResponseObject ( data = DashboardConfig . DashboardAPIKeys )
2024-08-16 05:20:29 +02:00
return ResponseObject ( False , " Dashboard API Keys function is disabled " )
2024-07-31 00:45:05 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/newDashboardAPIKey ' , methods = [ ' POST ' ] )
2024-07-31 00:45:05 +02:00
def API_newDashboardAPIKey ( ) :
data = request . get_json ( )
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
try :
if data [ ' neverExpire ' ] :
expiredAt = None
else :
2024-08-07 01:15:00 +02:00
expiredAt = datetime . strptime ( data [ ' ExpiredAt ' ] , ' % Y- % m- %d % H: % M: % S ' )
2024-07-31 00:45:05 +02:00
DashboardConfig . createAPIKeys ( expiredAt )
return ResponseObject ( True , data = DashboardConfig . DashboardAPIKeys )
except Exception as e :
return ResponseObject ( False , str ( e ) )
return ResponseObject ( False , " Dashboard API Keys function is disbaled " )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/deleteDashboardAPIKey ' , methods = [ ' POST ' ] )
2024-07-31 00:45:05 +02:00
def API_deleteDashboardAPIKey ( ) :
data = request . get_json ( )
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
if len ( data [ ' Key ' ] ) > 0 and len ( list ( filter ( lambda x : x . Key == data [ ' Key ' ] , DashboardConfig . DashboardAPIKeys ) ) ) > 0 :
DashboardConfig . deleteAPIKey ( data [ ' Key ' ] )
return ResponseObject ( True , data = DashboardConfig . DashboardAPIKeys )
return ResponseObject ( False , " Dashboard API Keys function is disbaled " )
2024-06-17 21:16:42 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/updatePeerSettings/<configName> ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_updatePeerSettings ( configName ) :
2021-04-03 02:48:00 +02:00
data = request . get_json ( )
id = data [ ' id ' ]
2024-06-17 21:16:42 +02:00
if len ( id ) > 0 and configName in WireguardConfigurations . keys ( ) :
name = data [ ' name ' ]
private_key = data [ ' private_key ' ]
dns_addresses = data [ ' DNS ' ]
allowed_ip = data [ ' allowed_ip ' ]
endpoint_allowed_ip = data [ ' endpoint_allowed_ip ' ]
preshared_key = data [ ' preshared_key ' ]
mtu = data [ ' mtu ' ]
keepalive = data [ ' keepalive ' ]
wireguardConfig = WireguardConfigurations [ configName ]
foundPeer , peer = wireguardConfig . searchPeer ( id )
if foundPeer :
return peer . updatePeer ( name , private_key , preshared_key , dns_addresses ,
allowed_ip , endpoint_allowed_ip , mtu , keepalive )
return ResponseObject ( False , " Peer does not exist " )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/resetPeerData/<configName> ' , methods = [ ' POST ' ] )
2024-08-09 05:27:13 +02:00
def API_resetPeerData ( configName ) :
data = request . get_json ( )
id = data [ ' id ' ]
type = data [ ' type ' ]
if len ( id ) == 0 or configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration/Peer does not exist " )
wgc = WireguardConfigurations . get ( configName )
foundPeer , peer = wgc . searchPeer ( id )
if not foundPeer :
return ResponseObject ( False , " Configuration/Peer does not exist " )
return ResponseObject ( status = peer . resetDataUsage ( type ) )
2024-06-17 21:16:42 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/deletePeers/<configName> ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_deletePeers ( configName : str ) - > ResponseObject :
data = request . get_json ( )
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
return ResponseObject ( False , " Please specify more than one peer " )
configuration = WireguardConfigurations . get ( configName )
return configuration . deletePeers ( peers )
2021-04-03 02:48:00 +02:00
2024-06-17 21:16:42 +02:00
return ResponseObject ( False , " Configuration does not exist " )
2021-12-26 00:26:39 +01:00
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/restrictPeers/<configName> ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_restrictPeers ( configName : str ) - > ResponseObject :
2021-04-03 02:48:00 +02:00
data = request . get_json ( )
2024-06-17 21:16:42 +02:00
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
return ResponseObject ( False , " Please specify more than one peer " )
configuration = WireguardConfigurations . get ( configName )
return configuration . restrictPeers ( peers )
return ResponseObject ( False , " Configuration does not exist " )
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/sharePeer/create ' , methods = [ ' POST ' ] )
2024-08-06 16:17:14 +02:00
def API_sharePeer_create ( ) :
data : dict [ str , str ] = request . get_json ( )
Configuration = data . get ( ' Configuration ' )
Peer = data . get ( ' Peer ' )
ExpireDate = data . get ( ' ExpireDate ' )
if Configuration is None or Peer is None :
return ResponseObject ( False , " Please specify configuration and peer " )
activeLink = AllPeerShareLinks . getLink ( Configuration , Peer )
if len ( activeLink ) > 0 :
return ResponseObject ( False , " This peer is already sharing, please stop sharing first. " )
status , message = AllPeerShareLinks . addLink ( Configuration , Peer , ExpireDate )
if not status :
return ResponseObject ( status , message )
return ResponseObject ( data = AllPeerShareLinks . getLinkByID ( message ) )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/sharePeer/update ' , methods = [ ' POST ' ] )
2024-08-06 16:17:14 +02:00
def API_sharePeer_update ( ) :
data : dict [ str , str ] = request . get_json ( )
ShareID : str = data . get ( " ShareID " )
ExpireDate : str = data . get ( " ExpireDate " )
if ShareID is None :
return ResponseObject ( False , " Please specify ShareID " )
if len ( AllPeerShareLinks . getLinkByID ( ShareID ) ) == 0 :
return ResponseObject ( False , " ShareID does not exist " )
status , message = AllPeerShareLinks . updateLinkExpireDate ( ShareID , ExpireDate )
if not status :
return ResponseObject ( status , message )
return ResponseObject ( data = AllPeerShareLinks . getLinkByID ( ShareID ) )
2024-06-17 21:16:42 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/sharePeer/get ' , methods = [ ' GET ' ] )
2024-08-07 06:37:05 +02:00
def API_sharePeer_get ( ) :
data = request . args
ShareID = data . get ( " ShareID " )
if ShareID is None or len ( ShareID ) == 0 :
return ResponseObject ( False , " Please provide ShareID " )
link = AllPeerShareLinks . getLinkByID ( ShareID )
if len ( link ) == 0 :
return ResponseObject ( False , " This link is either expired to invalid " )
l = link [ 0 ]
if l . Configuration not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " The peer you ' re looking for does not exist " )
c = WireguardConfigurations . get ( l . Configuration )
fp , p = c . searchPeer ( l . Peer )
if not fp :
return ResponseObject ( False , " The peer you ' re looking for does not exist " )
return ResponseObject ( data = p . downloadPeer ( ) )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/allowAccessPeers/<configName> ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_allowAccessPeers ( configName : str ) - > ResponseObject :
2021-08-06 05:15:50 +02:00
data = request . get_json ( )
2024-06-17 21:16:42 +02:00
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
return ResponseObject ( False , " Please specify more than one peer " )
configuration = WireguardConfigurations . get ( configName )
return configuration . allowAccessPeers ( peers )
return ResponseObject ( False , " Configuration does not exist " )
2021-08-14 23:13:16 +02:00
2021-12-24 03:26:24 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/addPeers/<configName> ' , methods = [ ' POST ' ] )
2024-06-17 21:16:42 +02:00
def API_addPeers ( configName ) :
data = request . get_json ( )
bulkAdd = data [ ' bulkAdd ' ]
bulkAddAmount = data [ ' bulkAddAmount ' ]
public_key = data [ ' public_key ' ]
allowed_ips = data [ ' allowed_ips ' ]
endpoint_allowed_ip = data [ ' endpoint_allowed_ip ' ]
dns_addresses = data [ ' DNS ' ]
mtu = data [ ' mtu ' ]
keep_alive = data [ ' keepalive ' ]
preshared_key = data [ ' preshared_key ' ]
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
if configName in WireguardConfigurations . keys ( ) :
config = WireguardConfigurations . get ( configName )
if ( not bulkAdd and ( len ( public_key ) == 0 or len ( allowed_ips ) == 0 ) ) or len ( endpoint_allowed_ip ) == 0 :
return ResponseObject ( False , " Please fill in all required box. " )
if not config . getStatus ( ) :
2024-08-05 23:55:58 +02:00
config . toggleConfiguration ( )
2024-06-17 21:16:42 +02:00
if bulkAdd :
if bulkAddAmount < 1 :
return ResponseObject ( False , " Please specify amount of peers you want to add " )
availableIps = _getWireguardConfigurationAvailableIP ( configName )
if not availableIps [ 0 ] :
return ResponseObject ( False , " No more available IP can assign " )
if bulkAddAmount > len ( availableIps [ 1 ] ) :
return ResponseObject ( False ,
f " The maximum number of peers can add is { len ( availableIps [ 1 ] ) } " )
keyPairs = [ ]
for i in range ( bulkAddAmount ) :
2024-08-09 05:27:13 +02:00
newPrivateKey = _generatePrivateKey ( ) [ 1 ]
keyPairs . append ( {
" private_key " : newPrivateKey ,
" id " : _generatePublicKey ( newPrivateKey ) [ 1 ] ,
" preshared_key " : _generatePrivateKey ( ) [ 1 ] ,
" allowed_ip " : availableIps [ 1 ] [ i ] ,
" name " : f " BulkPeer # { ( i + 1 ) } _ { datetime . now ( ) . strftime ( ' % Y % m %d _ % H % M % S ' ) } "
} )
2024-06-17 21:16:42 +02:00
if len ( keyPairs ) == 0 :
return ResponseObject ( False , " Generating key pairs by bulk failed " )
2024-08-09 05:27:13 +02:00
config . addPeers ( keyPairs )
for kp in keyPairs :
found , peer = config . searchPeer ( kp [ ' id ' ] )
2024-06-17 21:16:42 +02:00
if found :
2024-08-09 05:27:13 +02:00
if not peer . updatePeer ( kp [ ' name ' ] , kp [ ' private_key ' ] , kp [ ' preshared_key ' ] , dns_addresses ,
kp [ ' allowed_ip ' ] , endpoint_allowed_ip , mtu , keep_alive ) :
2024-06-17 21:16:42 +02:00
return ResponseObject ( False , " Failed to add peers in bulk " )
return ResponseObject ( )
2021-12-26 00:26:39 +01:00
2022-01-13 01:53:36 +01:00
else :
2024-06-17 21:16:42 +02:00
if config . searchPeer ( public_key ) [ 0 ] is True :
return ResponseObject ( False , f " This peer already exist. " )
name = data [ ' name ' ]
private_key = data [ ' private_key ' ]
2024-08-09 05:27:13 +02:00
config . addPeers ( [ { " id " : public_key , " allowed_ip " : ' ' . join ( allowed_ips ) } ] )
# subprocess.check_output(
# f"wg set {config.Name} peer {public_key} allowed-ips {''.join(allowed_ips)}",
# shell=True, stderr=subprocess.STDOUT)
# if len(preshared_key) > 0:
# subprocess.check_output(
# f"wg set {config.Name} peer {public_key} preshared-key {preshared_key}",
# shell=True, stderr=subprocess.STDOUT)
# subprocess.check_output(
# f"wg-quick save {config.Name}", shell=True, stderr=subprocess.STDOUT)
# config.getPeersList()
2024-06-17 21:16:42 +02:00
found , peer = config . searchPeer ( public_key )
if found :
return peer . updatePeer ( name , private_key , preshared_key , dns_addresses , " , " . join ( allowed_ips ) ,
endpoint_allowed_ip , mtu , keep_alive )
return ResponseObject ( False , " Configuration does not exist " )
2024-08-15 00:40:28 +02:00
@app.route ( f " { APP_PREFIX } /api/downloadPeer/<configName> " )
2024-06-17 21:16:42 +02:00
def API_downloadPeer ( configName ) :
data = request . args
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration or peer does not exist " )
configuration = WireguardConfigurations [ configName ]
peerFound , peer = configuration . searchPeer ( data [ ' id ' ] )
if len ( data [ ' id ' ] ) == 0 or not peerFound :
return ResponseObject ( False , " Configuration or peer does not exist " )
return ResponseObject ( data = peer . downloadPeer ( ) )
2024-08-15 00:40:28 +02:00
@app.route ( f " { APP_PREFIX } /api/downloadAllPeers/<configName> " )
2024-06-17 21:16:42 +02:00
def API_downloadAllPeers ( configName ) :
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration or peer does not exist " )
configuration = WireguardConfigurations [ configName ]
peerData = [ ]
untitledPeer = 0
for i in configuration . Peers :
file = i . downloadPeer ( )
if file [ " fileName " ] == " UntitledPeer_ " + configName :
file [ " fileName " ] = str ( untitledPeer ) + " _ " + file [ " fileName " ]
untitledPeer + = 1
peerData . append ( file )
return ResponseObject ( data = peerData )
2024-08-15 00:40:28 +02:00
@app.route ( f " { APP_PREFIX } /api/getAvailableIPs/<configName> " )
2024-06-17 21:16:42 +02:00
def API_getAvailableIPs ( configName ) :
status , ips = _getWireguardConfigurationAvailableIP ( configName )
return ResponseObject ( status = status , data = ips )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getWireguardConfigurationInfo ' , methods = [ " GET " ] )
2024-06-17 21:16:42 +02:00
def API_getConfigurationInfo ( ) :
configurationName = request . args . get ( " configurationName " )
if not configurationName or configurationName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please provide configuration name " )
return ResponseObject ( data = {
" configurationInfo " : WireguardConfigurations [ configurationName ] ,
" configurationPeers " : WireguardConfigurations [ configurationName ] . getPeersList ( ) ,
" configurationRestrictedPeers " : WireguardConfigurations [ configurationName ] . getRestrictedPeersList ( )
} )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getDashboardTheme ' )
2024-06-17 21:16:42 +02:00
def API_getDashboardTheme ( ) :
return ResponseObject ( data = DashboardConfig . GetConfig ( " Server " , " dashboard_theme " ) [ 1 ] )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/savePeerScheduleJob/ ' , methods = [ " POST " ] )
2024-06-25 17:02:13 +02:00
def API_savePeerScheduleJob ( ) :
data = request . json
if " Job " not in data . keys ( ) not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please specify job " )
job : dict = data [ ' Job ' ]
if " Peer " not in job . keys ( ) or " Configuration " not in job . keys ( ) :
return ResponseObject ( False , " Please specify peer and configuration " )
configuration = WireguardConfigurations . get ( job [ ' Configuration ' ] )
f , fp = configuration . searchPeer ( job [ ' Peer ' ] )
if not f :
return ResponseObject ( False , " Peer does not exist in this configuration " )
s , p = AllPeerJobs . saveJob ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
if s :
return ResponseObject ( s , data = p )
return ResponseObject ( s , message = p )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/deletePeerScheduleJob/ ' , methods = [ ' POST ' ] )
2024-06-30 18:58:02 +02:00
def API_deletePeerScheduleJob ( ) :
data = request . json
if " Job " not in data . keys ( ) not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please specify job " )
job : dict = data [ ' Job ' ]
if " Peer " not in job . keys ( ) or " Configuration " not in job . keys ( ) :
return ResponseObject ( False , " Please specify peer and configuration " )
configuration = WireguardConfigurations . get ( job [ ' Configuration ' ] )
f , fp = configuration . searchPeer ( job [ ' Peer ' ] )
if not f :
return ResponseObject ( False , " Peer does not exist in this configuration " )
s , p = AllPeerJobs . deleteJob ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
if s :
return ResponseObject ( s , data = p )
return ResponseObject ( s , message = p )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/getPeerScheduleJobLogs/<configName> ' , methods = [ ' GET ' ] )
2024-07-30 00:40:07 +02:00
def API_getPeerScheduleJobLogs ( configName ) :
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration does not exist " )
data = request . args . get ( " requestAll " )
requestAll = False
if data is not None and data == " true " :
requestAll = True
return ResponseObject ( data = JobLogger . getLogs ( requestAll , configName ) )
2024-06-30 18:58:02 +02:00
2024-06-25 17:02:13 +02:00
'''
Tools
'''
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/ping/getAllPeersIpAddress ' )
2024-06-17 21:16:42 +02:00
def API_ping_getAllPeersIpAddress ( ) :
ips = { }
for c in WireguardConfigurations . values ( ) :
cips = { }
for p in c . Peers :
allowed_ip = p . allowed_ip . replace ( " " , " " ) . split ( " , " )
parsed = [ ]
for x in allowed_ip :
ip = ipaddress . ip_network ( x , strict = False )
if len ( list ( ip . hosts ( ) ) ) == 1 :
parsed . append ( str ( ip . hosts ( ) [ 0 ] ) )
endpoint = p . endpoint . replace ( " " , " " ) . replace ( " (none) " , " " )
if len ( p . name ) > 0 :
cips [ f " { p . name } - { p . id } " ] = {
" allowed_ips " : parsed ,
" endpoint " : endpoint
}
2021-08-14 23:13:16 +02:00
else :
2024-06-17 21:16:42 +02:00
cips [ f " { p . id } " ] = {
" allowed_ips " : parsed ,
" endpoint " : endpoint
}
ips [ c . Name ] = cips
return ResponseObject ( data = ips )
2022-01-02 14:35:39 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/ping/execute ' )
2024-06-17 21:16:42 +02:00
def API_ping_execute ( ) :
if " ipAddress " in request . args . keys ( ) and " count " in request . args . keys ( ) :
ip = request . args [ ' ipAddress ' ]
count = request . args [ ' count ' ]
try :
if ip is not None and len ( ip ) > 0 and count is not None and count . isnumeric ( ) :
result = ping ( ip , count = int ( count ) , source = None )
return ResponseObject ( data = {
" address " : result . address ,
" is_alive " : result . is_alive ,
" min_rtt " : result . min_rtt ,
" avg_rtt " : result . avg_rtt ,
" max_rtt " : result . max_rtt ,
" package_sent " : result . packets_sent ,
" package_received " : result . packets_received ,
" package_loss " : result . packet_loss
} )
return ResponseObject ( False , " Please specify an IP Address (v4/v6) " )
except Exception as exp :
return ResponseObject ( False , exp )
return ResponseObject ( False , " Please provide ipAddress and count " )
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/traceroute/execute ' )
2024-06-17 21:16:42 +02:00
def API_traceroute_execute ( ) :
if " ipAddress " in request . args . keys ( ) and len ( request . args . get ( " ipAddress " ) ) > 0 :
ipAddress = request . args . get ( ' ipAddress ' )
try :
tracerouteResult = traceroute ( ipAddress )
result = [ ]
for hop in tracerouteResult :
if len ( result ) > 1 :
skipped = False
for i in range ( result [ - 1 ] [ " hop " ] + 1 , hop . distance ) :
result . append (
{
" hop " : i ,
" ip " : " * " ,
" avg_rtt " : " * " ,
" min_rtt " : " * " ,
" max_rtt " : " * "
}
)
skip = True
if skipped : continue
result . append (
{
" hop " : hop . distance ,
" ip " : hop . address ,
" avg_rtt " : hop . avg_rtt ,
" min_rtt " : hop . min_rtt ,
" max_rtt " : hop . max_rtt
} )
return ResponseObject ( data = result )
except Exception as exp :
return ResponseObject ( False , exp )
else :
return ResponseObject ( False , " Please provide ipAddress " )
2021-09-03 23:32:51 +02:00
2024-08-17 06:31:46 +02:00
@app.route ( f ' { APP_PREFIX } /api/getDashboardUpdate ' )
def API_getDashboardUpdate ( ) :
import urllib . request as req ;
try :
r = req . urlopen ( " https://api.github.com/repos/donaldzou/WGDashboard/releases/latest " , timeout = 5 ) . read ( )
data = dict ( json . loads ( r ) )
tagName = data . get ( ' tag_name ' )
htmlUrl = data . get ( ' html_url ' )
if tagName is not None and htmlUrl is not None :
if tagName != DASHBOARD_VERSION :
return ResponseObject ( message = f " { tagName } is now avaible for update! " , data = htmlUrl )
else :
return ResponseObject ( message = " You ' re on the latest version " )
return ResponseObject ( False )
except urllib . error . HTTPError as e :
return ResponseObject ( False , f " Request to GitHub API failed. Returned a { e . code } status. " )
2021-09-03 23:32:51 +02:00
2024-06-17 21:16:42 +02:00
'''
Sign Up
'''
2022-03-24 07:10:52 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/isTotpEnabled ' )
2024-06-17 21:16:42 +02:00
def API_isTotpEnabled ( ) :
2024-08-05 21:39:11 +02:00
return (
ResponseObject ( data = DashboardConfig . GetConfig ( " Account " , " enable_totp " ) [ 1 ] and DashboardConfig . GetConfig ( " Account " , " totp_verified " ) [ 1 ] ) )
2022-03-22 03:33:19 +01:00
2022-03-24 07:10:52 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/Welcome_GetTotpLink ' )
2024-06-17 21:16:42 +02:00
def API_Welcome_GetTotpLink ( ) :
2024-08-05 21:39:11 +02:00
if not DashboardConfig . GetConfig ( " Account " , " totp_verified " ) [ 1 ] :
DashboardConfig . SetConfig ( " Account " , " totp_key " , pyotp . random_base32 ( ) )
2024-06-17 21:16:42 +02:00
return ResponseObject (
data = pyotp . totp . TOTP ( DashboardConfig . GetConfig ( " Account " , " totp_key " ) [ 1 ] ) . provisioning_uri (
issuer_name = " WGDashboard " ) )
return ResponseObject ( False )
2022-03-24 07:10:52 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/Welcome_VerifyTotpLink ' , methods = [ " POST " ] )
2024-06-17 21:16:42 +02:00
def API_Welcome_VerifyTotpLink ( ) :
2023-11-28 22:37:16 +01:00
data = request . get_json ( )
2024-08-05 21:39:11 +02:00
totp = pyotp . TOTP ( DashboardConfig . GetConfig ( " Account " , " totp_key " ) [ 1 ] ) . now ( )
if totp == data [ ' totp ' ] :
DashboardConfig . SetConfig ( " Account " , " totp_verified " , " true " )
DashboardConfig . SetConfig ( " Account " , " enable_totp " , " true " )
return ResponseObject ( totp == data [ ' totp ' ] )
2023-11-28 22:37:16 +01:00
2022-04-23 06:34:11 +02:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } /api/Welcome_Finish ' , methods = [ " POST " ] )
2024-06-17 21:16:42 +02:00
def API_Welcome_Finish ( ) :
2022-04-23 06:34:11 +02:00
data = request . get_json ( )
2024-06-17 21:16:42 +02:00
if DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] :
if data [ " username " ] == " " :
return ResponseObject ( False , " Username cannot be blank. " )
2022-04-23 06:34:11 +02:00
2024-06-17 21:16:42 +02:00
if data [ " newPassword " ] == " " or len ( data [ " newPassword " ] ) < 8 :
return ResponseObject ( False , " Password must be at least 8 characters " )
2022-04-21 21:11:01 +02:00
2024-06-17 21:16:42 +02:00
updateUsername , updateUsernameErr = DashboardConfig . SetConfig ( " Account " , " username " , data [ " username " ] )
updatePassword , updatePasswordErr = DashboardConfig . SetConfig ( " Account " , " password " ,
{
" newPassword " : data [ " newPassword " ] ,
2024-08-02 23:27:28 +02:00
" repeatNewPassword " : data [ " repeatNewPassword " ] ,
2024-06-17 21:16:42 +02:00
" currentPassword " : " admin "
} )
2024-08-05 21:39:11 +02:00
# updateEnableTotp, updateEnableTotpErr = DashboardConfig.SetConfig("Account", "enable_totp", data["enable_totp"])
2021-12-26 00:26:39 +01:00
2024-08-05 21:39:11 +02:00
if not updateUsername or not updatePassword :
return ResponseObject ( False , f " { updateUsernameErr } , { updatePasswordErr } " . strip ( " , " ) )
2021-12-26 00:26:39 +01:00
2024-06-17 21:16:42 +02:00
DashboardConfig . SetConfig ( " Other " , " welcome_session " , False )
2022-01-02 14:35:39 +01:00
2024-06-17 21:16:42 +02:00
return ResponseObject ( )
2022-01-02 14:35:39 +01:00
2024-01-09 06:25:47 +01:00
2024-08-14 07:17:47 +02:00
@app.route ( f ' { APP_PREFIX } / ' , methods = [ ' GET ' ] )
2024-06-17 21:16:42 +02:00
def index ( ) :
2024-01-09 06:25:47 +01:00
"""
2024-06-17 21:16:42 +02:00
Index page related
@return : Template
2024-01-09 06:25:47 +01:00
"""
2024-08-15 00:40:28 +02:00
return render_template ( ' index.html ' , APP_PREFIX = APP_PREFIX )
2024-01-09 06:25:47 +01:00
2024-06-17 21:16:42 +02:00
def backGroundThread ( ) :
with app . app_context ( ) :
2024-08-05 02:17:29 +02:00
print ( f " [WGDashboard] Background Thread #1 Started " , flush = True )
2024-08-04 07:31:31 +02:00
time . sleep ( 10 )
2022-04-06 03:39:47 +02:00
while True :
2024-06-17 21:16:42 +02:00
for c in WireguardConfigurations . values ( ) :
if c . getStatus ( ) :
try :
c . getPeersTransfer ( )
c . getPeersLatestHandshake ( )
c . getPeersEndpoint ( )
2024-08-09 23:29:57 +02:00
c . getPeersList ( )
2024-06-17 21:16:42 +02:00
except Exception as e :
2024-08-05 02:17:29 +02:00
print ( f " [WGDashboard] Background Thread #1 Error: { str ( e ) } " , flush = True )
2024-06-17 21:16:42 +02:00
time . sleep ( 10 )
2022-01-02 14:35:39 +01:00
2021-10-18 01:24:09 +02:00
2024-06-30 18:58:02 +02:00
def peerJobScheduleBackgroundThread ( ) :
with app . app_context ( ) :
2024-08-05 02:17:29 +02:00
print ( f " [WGDashboard] Background Thread #2 Started " , flush = True )
2024-07-28 00:51:43 +02:00
time . sleep ( 10 )
2024-06-30 18:58:02 +02:00
while True :
AllPeerJobs . runJob ( )
2024-08-04 07:31:31 +02:00
time . sleep ( 180 )
2024-06-30 18:58:02 +02:00
2024-06-17 21:40:25 +02:00
def gunicornConfig ( ) :
_ , app_ip = DashboardConfig . GetConfig ( " Server " , " app_ip " )
_ , app_port = DashboardConfig . GetConfig ( " Server " , " app_port " )
return app_ip , app_port
2024-08-03 19:25:57 +02:00
2024-08-06 16:17:14 +02:00
AllPeerShareLinks : PeerShareLinks = PeerShareLinks ( )
2024-07-31 00:45:05 +02:00
AllPeerJobs : PeerJobs = PeerJobs ( )
2024-08-03 23:03:39 +02:00
JobLogger : PeerJobLogger = PeerJobLogger ( )
DashboardLogger : DashboardLogger = DashboardLogger ( )
2024-06-19 11:09:58 +02:00
_ , app_ip = DashboardConfig . GetConfig ( " Server " , " app_ip " )
_ , app_port = DashboardConfig . GetConfig ( " Server " , " app_port " )
_ , WG_CONF_PATH = DashboardConfig . GetConfig ( " Server " , " wg_conf_path " )
2024-08-03 19:25:57 +02:00
2024-08-14 07:17:47 +02:00
2024-08-03 19:25:57 +02:00
WireguardConfigurations : dict [ str , WireguardConfiguration ] = { }
2024-06-19 11:09:58 +02:00
WireguardConfigurations = _getConfigurationList ( )
2024-06-17 21:40:25 +02:00
2024-08-05 21:39:11 +02:00
def startThreads ( ) :
bgThread = threading . Thread ( target = backGroundThread )
bgThread . daemon = True
bgThread . start ( )
scheduleJobThread = threading . Thread ( target = peerJobScheduleBackgroundThread )
scheduleJobThread . daemon = True
scheduleJobThread . start ( )
2024-06-30 18:58:02 +02:00
2024-08-03 23:03:39 +02:00
2021-10-18 01:24:09 +02:00
if __name__ == " __main__ " :
2024-08-05 21:39:11 +02:00
startThreads ( )
2024-06-30 18:58:02 +02:00
app . run ( host = app_ip , debug = False , port = app_port )