mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2026-02-28 19:46:17 +00:00
v4.3.1 bug fix (#1149)
* Fix bugs * Fixes and improvements * Update comment * Reduce comment
This commit is contained in:
@@ -8,7 +8,7 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from flask import Flask, request, render_template, session, send_file
|
from flask import Flask, request, render_template, session, send_file, current_app
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from icmplib import ping, traceroute
|
from icmplib import ping, traceroute
|
||||||
from flask.json.provider import DefaultJSONProvider
|
from flask.json.provider import DefaultJSONProvider
|
||||||
@@ -254,7 +254,6 @@ def auth_req():
|
|||||||
whiteList = [
|
whiteList = [
|
||||||
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
|
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
|
||||||
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
|
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
|
||||||
'/fileDownload',
|
|
||||||
'/client',
|
'/client',
|
||||||
'/assets/', '/img/', '/json/',
|
'/assets/', '/img/', '/json/',
|
||||||
'/client/assets/', '/client/img/'
|
'/client/assets/', '/client/img/'
|
||||||
@@ -608,12 +607,19 @@ def API_deleteWireguardConfigurationBackup():
|
|||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/downloadWireguardConfigurationBackup')
|
||||||
def API_downloadWireguardConfigurationBackup():
|
def API_downloadWireguardConfigurationBackup():
|
||||||
configurationName = request.args.get('configurationName')
|
configurationName = os.path.basename(request.args.get('configurationName'))
|
||||||
backupFileName = request.args.get('backupFileName')
|
backupFileName = os.path.basename(request.args.get('backupFileName'))
|
||||||
|
|
||||||
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
|
status, zip = WireguardConfigurations[configurationName].downloadBackup(backupFileName)
|
||||||
return ResponseObject(status, data=zip, status_code=(200 if status else 404))
|
|
||||||
|
if not status:
|
||||||
|
current_app.logger.error(f"Failed to download a requested backup.\nConfiguration Name: {configurationName}\nBackup File Name: {backupFileName}")
|
||||||
|
return ResponseObject(False, "Internal server error", status_code=500)
|
||||||
|
|
||||||
|
return send_file(os.path.join('download', zip), as_attachment=True)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
||||||
def API_restoreWireguardConfigurationBackup():
|
def API_restoreWireguardConfigurationBackup():
|
||||||
@@ -969,8 +975,11 @@ def API_addPeers(configName):
|
|||||||
for i in allowed_ips:
|
for i in allowed_ips:
|
||||||
found = False
|
found = False
|
||||||
for subnet in availableIps.keys():
|
for subnet in availableIps.keys():
|
||||||
network = ipaddress.ip_network(subnet, False)
|
try:
|
||||||
ap = ipaddress.ip_network(i)
|
network = ipaddress.ip_network(subnet, False)
|
||||||
|
ap = ipaddress.ip_network(i)
|
||||||
|
except ValueError as e:
|
||||||
|
return ResponseObject(False, str(e))
|
||||||
if network.version == ap.version and ap.subnet_of(network):
|
if network.version == ap.version and ap.subnet_of(network):
|
||||||
found = True
|
found = True
|
||||||
|
|
||||||
@@ -994,8 +1003,7 @@ def API_addPeers(configName):
|
|||||||
return ResponseObject(status=status, message=message, data=addedPeers)
|
return ResponseObject(status=status, message=message, data=addedPeers)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error("Add peers failed", e)
|
app.logger.error("Add peers failed", e)
|
||||||
return ResponseObject(False,
|
return ResponseObject(False, f"Add peers failed.")
|
||||||
f"Add peers failed. Reason: {message}")
|
|
||||||
|
|
||||||
return ResponseObject(False, "Configuration does not exist")
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
|
||||||
@@ -1241,20 +1249,6 @@ def API_getPeerScheduleJobLogs(configName):
|
|||||||
requestAll = True
|
requestAll = True
|
||||||
return ResponseObject(data=AllPeerJobs.getPeerJobLogs(configName))
|
return ResponseObject(data=AllPeerJobs.getPeerJobLogs(configName))
|
||||||
|
|
||||||
'''
|
|
||||||
File Download
|
|
||||||
'''
|
|
||||||
@app.get(f'{APP_PREFIX}/fileDownload')
|
|
||||||
def API_download():
|
|
||||||
file = request.args.get('file')
|
|
||||||
if file is None or len(file) == 0:
|
|
||||||
return ResponseObject(False, "Please specify a file")
|
|
||||||
if os.path.exists(os.path.join('download', file)):
|
|
||||||
return send_file(os.path.join('download', file), as_attachment=True)
|
|
||||||
else:
|
|
||||||
return ResponseObject(False, "File does not exist")
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Tools
|
Tools
|
||||||
'''
|
'''
|
||||||
@@ -1742,4 +1736,4 @@ def index():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
startThreads()
|
startThreads()
|
||||||
DashboardPlugins.startThreads()
|
DashboardPlugins.startThreads()
|
||||||
app.run(host=app_ip, debug=False, port=app_port)
|
app.run(host=app_ip, debug=False, port=app_port)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from flask import current_app
|
|||||||
from .PeerJobs import PeerJobs
|
from .PeerJobs import PeerJobs
|
||||||
from .AmneziaPeer import AmneziaPeer
|
from .AmneziaPeer import AmneziaPeer
|
||||||
from .PeerShareLinks import PeerShareLinks
|
from .PeerShareLinks import PeerShareLinks
|
||||||
from .Utilities import RegexMatch
|
from .Utilities import RegexMatch, CheckAddress
|
||||||
from .WireguardConfiguration import WireguardConfiguration
|
from .WireguardConfiguration import WireguardConfiguration
|
||||||
from .DashboardWebHooks import DashboardWebHooks
|
from .DashboardWebHooks import DashboardWebHooks
|
||||||
|
|
||||||
@@ -276,13 +276,22 @@ class AmneziaConfiguration(WireguardConfiguration):
|
|||||||
with open(uid, "w+") as f:
|
with open(uid, "w+") as f:
|
||||||
f.write(p['preshared_key'])
|
f.write(p['preshared_key'])
|
||||||
|
|
||||||
subprocess.check_output(
|
newAllowedIPs = p['allowed_ip'].replace(" ", "")
|
||||||
f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
|
if not CheckAddress(newAllowedIPs):
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
return False, [], "Allowed IPs entry format is incorrect"
|
||||||
|
|
||||||
|
if not re.match(r"^[A-Za-z0-9+/]{42}[A-Ea-e0-9]=$", p["id"]):
|
||||||
|
return False, [], "Peer key format is incorrect"
|
||||||
|
|
||||||
|
command = [self.Protocol, "set", self.Name, "peer", p['id'], "allowed-ips", newAllowedIPs, "preshared-key", uid if presharedKeyExist else "/dev/null"]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if presharedKeyExist:
|
if presharedKeyExist:
|
||||||
os.remove(uid)
|
os.remove(uid)
|
||||||
subprocess.check_output(
|
|
||||||
f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
|
command = [f"{self.Protocol}-quick", "save", self.Name]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
self.getPeers()
|
self.getPeers()
|
||||||
for p in peers:
|
for p in peers:
|
||||||
p = self.searchPeer(p['id'])
|
p = self.searchPeer(p['id'])
|
||||||
@@ -294,7 +303,7 @@ class AmneziaConfiguration(WireguardConfiguration):
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error("Add peers error", e)
|
current_app.logger.error("Add peers error", e)
|
||||||
return False, [], str(e)
|
return False, [], "Internal server error"
|
||||||
return True, result['peers'], ""
|
return True, result['peers'], ""
|
||||||
|
|
||||||
def getRestrictedPeers(self):
|
def getRestrictedPeers(self):
|
||||||
@@ -302,4 +311,4 @@ class AmneziaConfiguration(WireguardConfiguration):
|
|||||||
with self.engine.connect() as conn:
|
with self.engine.connect() as conn:
|
||||||
restricted = conn.execute(self.peersRestrictedTable.select()).mappings().fetchall()
|
restricted = conn.execute(self.peersRestrictedTable.select()).mappings().fetchall()
|
||||||
for i in restricted:
|
for i in restricted:
|
||||||
self.RestrictedPeers.append(AmneziaPeer(i, self))
|
self.RestrictedPeers.append(AmneziaPeer(i, self))
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
from flask import current_app
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -77,18 +78,25 @@ class AmneziaPeer(Peer):
|
|||||||
f.write(preshared_key)
|
f.write(preshared_key)
|
||||||
|
|
||||||
newAllowedIPs = allowed_ip.replace(" ", "")
|
newAllowedIPs = allowed_ip.replace(" ", "")
|
||||||
updateAllowedIp = subprocess.check_output(
|
if not CheckAddress(newAllowedIPs):
|
||||||
f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if psk_exist else 'preshared-key /dev/null'}",
|
return False, "Allowed IPs entry format is incorrect"
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
|
||||||
|
command = [self.configuration.Protocol, "set", self.configuration.Name, "peer", self.id, "allowed-ips", newAllowedIPs, "preshared-key", uid if psk_exist else "/dev/null"]
|
||||||
|
|
||||||
|
updateAllowedIp = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if psk_exist: os.remove(uid)
|
if psk_exist: os.remove(uid)
|
||||||
|
|
||||||
if len(updateAllowedIp.decode().strip("\n")) != 0:
|
if len(updateAllowedIp.decode().strip("\n")) != 0:
|
||||||
return False, "Update peer failed when updating Allowed IPs"
|
current_app.logger.error(f"Update peer failed when updating Allowed IPs.\nInput: {newAllowedIPs}\nOutput: {updateAllowedIp.decode().strip('\n')}")
|
||||||
saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
|
return False, "Internal server error"
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
|
||||||
|
command = [f"{self.configuration.Protocol}-quick", "save", self.configuration.Name]
|
||||||
|
saveConfig = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
|
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
|
||||||
return False, "Update peer failed when saving the configuration"
|
current_app.logger.error("Update peer failed when saving the configuration")
|
||||||
|
return False, "Internal server error"
|
||||||
|
|
||||||
with self.configuration.engine.begin() as conn:
|
with self.configuration.engine.begin() as conn:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
@@ -108,4 +116,5 @@ class AmneziaPeer(Peer):
|
|||||||
self.configuration.getPeers()
|
self.configuration.getPeers()
|
||||||
return True, None
|
return True, None
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
return False, exc.output.decode("UTF-8").strip()
|
current_app.logger.error(f"Subprocess call failed:\n{exc.output.decode("UTF-8")}")
|
||||||
|
return False, "Internal server error"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from datetime import timedelta
|
|||||||
import jinja2
|
import jinja2
|
||||||
import sqlalchemy as db
|
import sqlalchemy as db
|
||||||
from .PeerJob import PeerJob
|
from .PeerJob import PeerJob
|
||||||
|
from flask import current_app
|
||||||
from .PeerShareLink import PeerShareLink
|
from .PeerShareLink import PeerShareLink
|
||||||
from .Utilities import GenerateWireguardPublicKey, CheckAddress, ValidateDNSAddress
|
from .Utilities import GenerateWireguardPublicKey, CheckAddress, ValidateDNSAddress
|
||||||
|
|
||||||
@@ -114,17 +115,25 @@ class Peer:
|
|||||||
f.write(preshared_key)
|
f.write(preshared_key)
|
||||||
|
|
||||||
newAllowedIPs = allowed_ip.replace(" ", "")
|
newAllowedIPs = allowed_ip.replace(" ", "")
|
||||||
updateAllowedIp = subprocess.check_output(
|
if not CheckAddress(newAllowedIPs):
|
||||||
f"{self.configuration.Protocol} set {self.configuration.Name} peer {self.id} allowed-ips {newAllowedIPs} {f'preshared-key {uid}' if psk_exist else 'preshared-key /dev/null'}",
|
return False, "Allowed IPs entry format is incorrect"
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
|
||||||
|
command = [self.configuration.Protocol, "set", self.configuration.Name, "peer", self.id, "allowed-ips", newAllowedIPs, "preshared-key", uid if psk_exist else "/dev/null"]
|
||||||
|
updateAllowedIp = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if psk_exist: os.remove(uid)
|
if psk_exist: os.remove(uid)
|
||||||
|
|
||||||
if len(updateAllowedIp.decode().strip("\n")) != 0:
|
if len(updateAllowedIp.decode().strip("\n")) != 0:
|
||||||
return False, "Update peer failed when updating Allowed IPs"
|
current_app.logger.error("Update peer failed when updating Allowed IPs")
|
||||||
saveConfig = subprocess.check_output(f"{self.configuration.Protocol}-quick save {self.configuration.Name}",
|
return False, "Internal server error"
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
|
||||||
|
command = [f"{self.configuration.Protocol}-quick", "save", self.configuration.Name]
|
||||||
|
saveConfig = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
|
if f"wg showconf {self.configuration.Name}" not in saveConfig.decode().strip('\n'):
|
||||||
return False, "Update peer failed when saving the configuration"
|
current_app.logger.error("Update peer failed when saving the configuration")
|
||||||
|
return False, "Internal server error"
|
||||||
|
|
||||||
with self.configuration.engine.begin() as conn:
|
with self.configuration.engine.begin() as conn:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
self.configuration.peersTable.update().values({
|
self.configuration.peersTable.update().values({
|
||||||
@@ -142,7 +151,8 @@ class Peer:
|
|||||||
)
|
)
|
||||||
return True, None
|
return True, None
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
return False, exc.output.decode("UTF-8").strip()
|
current_app.logger.error(f"Subprocess call failed:\n{exc.output.decode("UTF-8")}")
|
||||||
|
return False, "Internal server error"
|
||||||
|
|
||||||
def downloadPeer(self) -> dict[str, str]:
|
def downloadPeer(self) -> dict[str, str]:
|
||||||
final = {
|
final = {
|
||||||
@@ -153,12 +163,14 @@ class Peer:
|
|||||||
if len(filename) == 0:
|
if len(filename) == 0:
|
||||||
filename = "UntitledPeer"
|
filename = "UntitledPeer"
|
||||||
filename = "".join(filename.split(' '))
|
filename = "".join(filename.split(' '))
|
||||||
filename = f"{filename}"
|
|
||||||
illegal_filename = [".", ",", "/", "?", "<", ">", "\\", ":", "*", '|' '\"', "com1", "com2", "com3",
|
# use previous filtering code if code below is insufficient or faulty
|
||||||
"com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4",
|
filename = re.sub(r'[.,/?<>\\:*|"]', '', filename).rstrip(". ") # remove special characters
|
||||||
"lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn"]
|
|
||||||
for i in illegal_filename:
|
reserved_pattern = r"^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\..*)?$" # match com1-9, lpt1-9, con, nul, prn, aux, nul
|
||||||
filename = filename.replace(i, "")
|
|
||||||
|
if re.match(reserved_pattern, filename, re.IGNORECASE):
|
||||||
|
filename = f"file_{filename}" # prepend "file_" if it matches
|
||||||
|
|
||||||
for i in filename:
|
for i in filename:
|
||||||
if re.match("^[a-zA-Z0-9_=+.-]$", i):
|
if re.match("^[a-zA-Z0-9_=+.-]$", i):
|
||||||
@@ -379,4 +391,4 @@ class Peer:
|
|||||||
|
|
||||||
hours, remainder = divmod(delta.total_seconds(), 3600)
|
hours, remainder = divmod(delta.total_seconds(), 3600)
|
||||||
minutes, seconds = divmod(remainder, 60)
|
minutes, seconds = divmod(remainder, 60)
|
||||||
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
|
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ def CheckAddress(ips_str: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def CheckPeerKey(peer_key: str) -> bool:
|
||||||
|
return re.match(r"^[A-Za-z0-9+/]{43}=$", peer_key)
|
||||||
|
|
||||||
def ValidateDNSAddress(addresses_str: str) -> tuple[bool, str | None]:
|
def ValidateDNSAddress(addresses_str: str) -> tuple[bool, str | None]:
|
||||||
if len(addresses_str) == 0:
|
if len(addresses_str) == 0:
|
||||||
return False, "Got an empty list/string to check for valid DNS-addresses"
|
return False, "Got an empty list/string to check for valid DNS-addresses"
|
||||||
@@ -110,4 +113,4 @@ def ValidatePasswordStrength(password: str) -> tuple[bool, str] | tuple[bool, No
|
|||||||
if not re.search(r'[$&+,:;=?@#|\'<>.\-^*()%!~_-]', password):
|
if not re.search(r'[$&+,:;=?@#|\'<>.\-^*()%!~_-]', password):
|
||||||
return False, "Password must contain at least 1 special character from $&+,:;=?@#|'<>.-^*()%!~_-"
|
return False, "Password must contain at least 1 special character from $&+,:;=?@#|'<>.-^*()%!~_-"
|
||||||
|
|
||||||
return True, None
|
return True, None
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ from .Utilities import StringToBoolean, \
|
|||||||
GenerateWireguardPublicKey, \
|
GenerateWireguardPublicKey, \
|
||||||
RegexMatch, \
|
RegexMatch, \
|
||||||
ValidateDNSAddress, \
|
ValidateDNSAddress, \
|
||||||
ValidateEndpointAllowedIPs
|
ValidateEndpointAllowedIPs, \
|
||||||
|
CheckAddress, \
|
||||||
|
CheckPeerKey
|
||||||
from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass
|
from .WireguardConfigurationInfo import WireguardConfigurationInfo, PeerGroupsClass
|
||||||
from .DashboardWebHooks import DashboardWebHooks
|
from .DashboardWebHooks import DashboardWebHooks
|
||||||
|
|
||||||
@@ -545,12 +547,22 @@ class WireguardConfiguration:
|
|||||||
with open(uid, "w+") as f:
|
with open(uid, "w+") as f:
|
||||||
f.write(p['preshared_key'])
|
f.write(p['preshared_key'])
|
||||||
|
|
||||||
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {p['id']} allowed-ips {p['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
|
newAllowedIPs = p['allowed_ip'].replace(" ", "")
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
if not CheckAddress(newAllowedIPs):
|
||||||
|
return False, [], "Allowed IPs entry format is incorrect"
|
||||||
|
|
||||||
|
if not CheckPeerKey(p["id"]):
|
||||||
|
return False, [], "Peer key format is incorrect"
|
||||||
|
|
||||||
|
command = [self.Protocol, "set", self.Name, "peer", p['id'], "allowed-ips", newAllowedIPs, "preshared-key", uid if presharedKeyExist else "/dev/null"]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if presharedKeyExist:
|
if presharedKeyExist:
|
||||||
os.remove(uid)
|
os.remove(uid)
|
||||||
subprocess.check_output(
|
|
||||||
f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
|
command = [f"{self.Protocol}-quick", "save", self.Name]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
self.getPeers()
|
self.getPeers()
|
||||||
for p in peers:
|
for p in peers:
|
||||||
p = self.searchPeer(p['id'])
|
p = self.searchPeer(p['id'])
|
||||||
@@ -562,7 +574,7 @@ class WireguardConfiguration:
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error("Add peers error", e)
|
current_app.logger.error("Add peers error", e)
|
||||||
return False, [], str(e)
|
return False, [], "Internal server error"
|
||||||
return True, result['peers'], ""
|
return True, result['peers'], ""
|
||||||
|
|
||||||
def searchPeer(self, publicKey):
|
def searchPeer(self, publicKey):
|
||||||
@@ -600,8 +612,16 @@ class WireguardConfiguration:
|
|||||||
with open(uid, "w+") as f:
|
with open(uid, "w+") as f:
|
||||||
f.write(restrictedPeer['preshared_key'])
|
f.write(restrictedPeer['preshared_key'])
|
||||||
|
|
||||||
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {restrictedPeer['id']} allowed-ips {restrictedPeer['allowed_ip'].replace(' ', '')}{f' preshared-key {uid}' if presharedKeyExist else ''}",
|
newAllowedIPs = restrictedPeer['allowed_ip'].replace(" ", "")
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
if not CheckAddress(newAllowedIPs):
|
||||||
|
return False, "Allowed IPs entry format is incorrect"
|
||||||
|
|
||||||
|
if not CheckPeerKey(restrictedPeer["id"]):
|
||||||
|
return False, "Peer key format is incorrect"
|
||||||
|
|
||||||
|
command = [self.Protocol, "set", self.Name, "peer", restrictedPeer["id"], "allowed-ips", newAllowedIPs, "preshared-key", uid if presharedKeyExist else "/dev/null"]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if presharedKeyExist: os.remove(uid)
|
if presharedKeyExist: os.remove(uid)
|
||||||
else:
|
else:
|
||||||
return False, "Failed to allow access of peer " + i
|
return False, "Failed to allow access of peer " + i
|
||||||
@@ -621,8 +641,9 @@ class WireguardConfiguration:
|
|||||||
found, pf = self.searchPeer(p)
|
found, pf = self.searchPeer(p)
|
||||||
if found:
|
if found:
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(f"{self.Protocol} set {self.Name} peer {pf.id} remove",
|
command = [self.Protocol, "set", self.Name, "peer", pf.id, "remove"]
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
conn.execute(
|
conn.execute(
|
||||||
self.peersRestrictedTable.insert().from_select(
|
self.peersRestrictedTable.insert().from_select(
|
||||||
[c.name for c in self.peersTable.columns],
|
[c.name for c in self.peersTable.columns],
|
||||||
@@ -703,17 +724,20 @@ class WireguardConfiguration:
|
|||||||
|
|
||||||
def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]:
|
def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(f"{self.Protocol}-quick save {self.Name}", shell=True, stderr=subprocess.STDOUT)
|
command = [f"{self.Protocol}-quick", "save", self.Name]
|
||||||
|
subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
return True, None
|
return True, None
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
return False, str(e)
|
current_app.logger.error(f"Failed to process command:\n{str(e)}")
|
||||||
|
return False, "Internal server error"
|
||||||
|
|
||||||
def getPeersLatestHandshake(self):
|
def getPeersLatestHandshake(self):
|
||||||
if not self.getStatus():
|
if not self.getStatus():
|
||||||
self.toggleConfiguration()
|
self.toggleConfiguration()
|
||||||
try:
|
try:
|
||||||
latestHandshake = subprocess.check_output(f"{self.Protocol} show {self.Name} latest-handshakes",
|
command = [self.Protocol, "show", self.Name, "latest-handshakes"]
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
latestHandshake = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
return "stopped"
|
return "stopped"
|
||||||
latestHandshake = latestHandshake.decode("UTF-8").split()
|
latestHandshake = latestHandshake.decode("UTF-8").split()
|
||||||
@@ -752,8 +776,9 @@ class WireguardConfiguration:
|
|||||||
if not self.getStatus():
|
if not self.getStatus():
|
||||||
self.toggleConfiguration()
|
self.toggleConfiguration()
|
||||||
# try:
|
# try:
|
||||||
data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} transfer",
|
command = [self.Protocol, "show", self.Name, "transfer"]
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
data_usage = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
data_usage = data_usage.decode("UTF-8").split("\n")
|
data_usage = data_usage.decode("UTF-8").split("\n")
|
||||||
|
|
||||||
data_usage = [p.split("\t") for p in data_usage]
|
data_usage = [p.split("\t") for p in data_usage]
|
||||||
@@ -808,10 +833,11 @@ class WireguardConfiguration:
|
|||||||
if not self.getStatus():
|
if not self.getStatus():
|
||||||
self.toggleConfiguration()
|
self.toggleConfiguration()
|
||||||
try:
|
try:
|
||||||
data_usage = subprocess.check_output(f"{self.Protocol} show {self.Name} endpoints",
|
command = [self.Protocol, "show", self.Name, "endpoints"]
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
data_usage = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
return "stopped"
|
return "stopped"
|
||||||
|
|
||||||
data_usage = data_usage.decode("UTF-8").split()
|
data_usage = data_usage.decode("UTF-8").split()
|
||||||
count = 0
|
count = 0
|
||||||
with self.engine.begin() as conn:
|
with self.engine.begin() as conn:
|
||||||
@@ -829,14 +855,17 @@ class WireguardConfiguration:
|
|||||||
self.getStatus()
|
self.getStatus()
|
||||||
if self.Status:
|
if self.Status:
|
||||||
try:
|
try:
|
||||||
check = subprocess.check_output(f"{self.Protocol}-quick down {self.Name}",
|
command = [f"{self.Protocol}-quick", "down", self.Name]
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
check = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
self.removeAutostart()
|
self.removeAutostart()
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
return False, str(exc.output.strip().decode("utf-8"))
|
return False, str(exc.output.strip().decode("utf-8"))
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
check = subprocess.check_output(f"{self.Protocol}-quick up {self.Name}", shell=True, stderr=subprocess.STDOUT)
|
command = [f"{self.Protocol}-quick", "up", self.Name]
|
||||||
|
check = subprocess.check_output(command, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
self.addAutostart()
|
self.addAutostart()
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
return False, str(exc.output.strip().decode("utf-8"))
|
return False, str(exc.output.strip().decode("utf-8"))
|
||||||
@@ -1015,31 +1044,33 @@ class WireguardConfiguration:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def renameConfiguration(self, newConfigurationName) -> tuple[bool, str]:
|
def renameConfiguration(self, newConfigurationName) -> tuple[bool, str]:
|
||||||
|
newConfigurationName = os.path.basename(newConfigurationName)
|
||||||
|
|
||||||
|
if len(newConfigurationName) > 15 or not re.match(r'^[a-zA-Z0-9_=\+\.\-]{1,15}$', newConfigurationName):
|
||||||
|
return False, "Configuration name is either too long or contains an illegal character"
|
||||||
|
|
||||||
|
newConfigurationName = newConfigurationName.replace("`", "") # double check
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.getStatus():
|
if self.getStatus():
|
||||||
self.toggleConfiguration()
|
self.toggleConfiguration()
|
||||||
self.createDatabase(newConfigurationName)
|
self.createDatabase(newConfigurationName)
|
||||||
with self.engine.begin() as conn:
|
with self.engine.begin() as conn:
|
||||||
conn.execute(
|
def doRenameStatement(suffix):
|
||||||
sqlalchemy.text(
|
newConfig = f"{newConfigurationName}{suffix}"
|
||||||
f'INSERT INTO "{newConfigurationName}" SELECT * FROM "{self.Name}"'
|
oldConfig = f"{self.Name}{suffix}"
|
||||||
|
|
||||||
|
conn.execute(
|
||||||
|
sqlalchemy.text(
|
||||||
|
f'INSERT INTO `{newConfig}` SELECT * FROM `{oldConfig}`'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
conn.execute(
|
doRenameStatement("")
|
||||||
sqlalchemy.text(
|
doRenameStatement("_restrict_access")
|
||||||
f'INSERT INTO "{newConfigurationName}_restrict_access" SELECT * FROM "{self.Name}_restrict_access"'
|
doRenameStatement("_deleted")
|
||||||
)
|
doRenameStatement("_transfer")
|
||||||
)
|
|
||||||
conn.execute(
|
|
||||||
sqlalchemy.text(
|
|
||||||
f'INSERT INTO "{newConfigurationName}_deleted" SELECT * FROM "{self.Name}_deleted"'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
conn.execute(
|
|
||||||
sqlalchemy.text(
|
|
||||||
f'INSERT INTO "{newConfigurationName}_transfer" SELECT * FROM "{self.Name}_transfer"'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
|
self.AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
self.configPath,
|
self.configPath,
|
||||||
@@ -1047,8 +1078,8 @@ class WireguardConfiguration:
|
|||||||
)
|
)
|
||||||
self.deleteConfiguration()
|
self.deleteConfiguration()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_stack()
|
current_app.logger.error(f"Failed to rename configuration.\nNew Configuration Name: {newConfigurationName}\nError: {str(e)}")
|
||||||
return False, str(e)
|
return False, "Internal server error"
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def getNumberOfAvailableIP(self):
|
def getNumberOfAvailableIP(self):
|
||||||
|
|||||||
@@ -46,14 +46,7 @@ const restoreBackup = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const downloadBackup = () => {
|
const downloadBackup = () => {
|
||||||
fetchGet("/api/downloadWireguardConfigurationBackup", {
|
window.location.href = getUrl(`/api/downloadWireguardConfigurationBackup?configurationName=${route.params.id}&backupFileName=${props.b.filename}`);
|
||||||
configurationName: route.params.id,
|
|
||||||
backupFileName: props.b.filename
|
|
||||||
}, (res) => {
|
|
||||||
if (res.status){
|
|
||||||
window.open(getUrl(`/fileDownload?file=${res.data}`), '_blank')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const delaySeconds = computed(() => {
|
const delaySeconds = computed(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user