v4.3.1 bug fix (#1149)
Some checks failed
Docker Build and Push / docker_build (push) Has been cancelled
Docker Build and Push / docker_scan (push) Has been cancelled

* Fix bugs

* Fixes and improvements

* Update comment

* Reduce comment
This commit is contained in:
mahemium
2026-02-26 11:15:52 +00:00
committed by GitHub
parent 06097316ec
commit ba4ac7c1ec
7 changed files with 157 additions and 106 deletions

View File

@@ -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)

View File

@@ -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))

View File

@@ -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"

View File

@@ -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}"

View File

@@ -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

View File

@@ -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):

View File

@@ -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(() => {