Preserve peers on config upload
- Preserve uploaded configs with peer blocks by saving raw content - Populate peers from file via existing getPeers() logic - Add path validation and basic content checks for safety - Inform users in UI when peers are preserved from uploadpull/1111/head
parent
0f98bb2537
commit
6f71fc201f
|
|
@ -392,32 +392,35 @@ def API_addWireguardConfiguration():
|
|||
f"Already have a configuration with the address \"{data['Address']}\"",
|
||||
"Address")
|
||||
|
||||
if "Backup" in data.keys():
|
||||
path = {
|
||||
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
||||
}
|
||||
|
||||
if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "wg"
|
||||
elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "awg"
|
||||
try:
|
||||
if "Backup" in data.keys():
|
||||
path = {
|
||||
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
||||
}
|
||||
|
||||
if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "wg"
|
||||
elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||
os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||
protocol = "awg"
|
||||
else:
|
||||
return ResponseObject(False, "Backup does not exist")
|
||||
|
||||
shutil.copy(
|
||||
os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
|
||||
os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
|
||||
)
|
||||
WireguardConfigurations[data['ConfigurationName']] = (
|
||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else (
|
||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
|
||||
else:
|
||||
return ResponseObject(False, "Backup does not exist")
|
||||
|
||||
shutil.copy(
|
||||
os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
|
||||
os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
|
||||
)
|
||||
WireguardConfigurations[data['ConfigurationName']] = (
|
||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else (
|
||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
|
||||
else:
|
||||
WireguardConfigurations[data['ConfigurationName']] = (
|
||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else (
|
||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
|
||||
WireguardConfigurations[data['ConfigurationName']] = (
|
||||
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else (
|
||||
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
|
||||
except WireguardConfiguration.InvalidConfigurationFileException as exc:
|
||||
return ResponseObject(False, str(exc))
|
||||
return ResponseObject()
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
|
||||
|
|
@ -1703,4 +1706,4 @@ def index():
|
|||
if __name__ == "__main__":
|
||||
startThreads()
|
||||
DashboardPlugins.startThreads()
|
||||
app.run(host=app_ip, debug=False, port=app_port)
|
||||
app.run(host=app_ip, debug=False, port=app_port)
|
||||
|
|
|
|||
|
|
@ -91,35 +91,58 @@ class WireguardConfiguration:
|
|||
else:
|
||||
setattr(self, i, str(data[i]))
|
||||
|
||||
self.__parser["Interface"] = {
|
||||
"PrivateKey": self.PrivateKey,
|
||||
"Address": self.Address,
|
||||
"ListenPort": self.ListenPort,
|
||||
"PreUp": f"{self.PreUp}",
|
||||
"PreDown": f"{self.PreDown}",
|
||||
"PostUp": f"{self.PostUp}",
|
||||
"PostDown": f"{self.PostDown}",
|
||||
"SaveConfig": "true"
|
||||
}
|
||||
raw_configuration = data.get("RawConfiguration") if data else None
|
||||
if raw_configuration:
|
||||
try:
|
||||
if not self.__isPathWithinBase(self.configPath, self.__getProtocolPath()):
|
||||
raise self.InvalidConfigurationFileException("Configuration path is invalid")
|
||||
if not raw_configuration.lstrip().startswith("[Interface]"):
|
||||
raise self.InvalidConfigurationFileException("Invalid configuration file: missing [Interface]")
|
||||
if any(ord(ch) == 0 for ch in raw_configuration):
|
||||
raise self.InvalidConfigurationFileException("Invalid configuration file: binary content detected")
|
||||
self.createDatabase()
|
||||
with open(self.configPath, "w+") as configFile:
|
||||
configFile.write(raw_configuration)
|
||||
current_app.logger.info(f"Configuration file {self.configPath} created from upload")
|
||||
self.__parseConfigurationFile()
|
||||
self.__initPeersList()
|
||||
except Exception:
|
||||
try:
|
||||
os.remove(self.configPath)
|
||||
except OSError:
|
||||
pass
|
||||
self.__dropDatabase()
|
||||
raise
|
||||
else:
|
||||
self.__parser["Interface"] = {
|
||||
"PrivateKey": self.PrivateKey,
|
||||
"Address": self.Address,
|
||||
"ListenPort": self.ListenPort,
|
||||
"PreUp": f"{self.PreUp}",
|
||||
"PreDown": f"{self.PreDown}",
|
||||
"PostUp": f"{self.PostUp}",
|
||||
"PostDown": f"{self.PostDown}",
|
||||
"SaveConfig": "true"
|
||||
}
|
||||
|
||||
if self.Protocol == 'awg':
|
||||
self.__parser["Interface"]["Jc"] = self.Jc
|
||||
self.__parser["Interface"]["Jc"] = self.Jc
|
||||
self.__parser["Interface"]["Jmin"] = self.Jmin
|
||||
self.__parser["Interface"]["Jmax"] = self.Jmax
|
||||
self.__parser["Interface"]["S1"] = self.S1
|
||||
self.__parser["Interface"]["S2"] = self.S2
|
||||
self.__parser["Interface"]["H1"] = self.H1
|
||||
self.__parser["Interface"]["H2"] = self.H2
|
||||
self.__parser["Interface"]["H3"] = self.H3
|
||||
self.__parser["Interface"]["H4"] = self.H4
|
||||
if self.Protocol == 'awg':
|
||||
self.__parser["Interface"]["Jc"] = self.Jc
|
||||
self.__parser["Interface"]["Jc"] = self.Jc
|
||||
self.__parser["Interface"]["Jmin"] = self.Jmin
|
||||
self.__parser["Interface"]["Jmax"] = self.Jmax
|
||||
self.__parser["Interface"]["S1"] = self.S1
|
||||
self.__parser["Interface"]["S2"] = self.S2
|
||||
self.__parser["Interface"]["H1"] = self.H1
|
||||
self.__parser["Interface"]["H2"] = self.H2
|
||||
self.__parser["Interface"]["H3"] = self.H3
|
||||
self.__parser["Interface"]["H4"] = self.H4
|
||||
|
||||
if "Backup" not in data.keys():
|
||||
self.createDatabase()
|
||||
with open(self.configPath, "w+") as configFile:
|
||||
self.__parser.write(configFile)
|
||||
current_app.logger.info(f"Configuration file {self.configPath} created")
|
||||
self.__initPeersList()
|
||||
if "Backup" not in data.keys():
|
||||
self.createDatabase()
|
||||
with open(self.configPath, "w+") as configFile:
|
||||
self.__parser.write(configFile)
|
||||
current_app.logger.info(f"Configuration file {self.configPath} created")
|
||||
self.__initPeersList()
|
||||
|
||||
if not os.path.exists(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup')):
|
||||
os.mkdir(os.path.join(self.__getProtocolPath(), 'WGDashboard_Backup'))
|
||||
|
|
@ -147,6 +170,12 @@ class WireguardConfiguration:
|
|||
else self.DashboardConfig.GetConfig("Server", "awg_conf_path")
|
||||
return path
|
||||
|
||||
def __isPathWithinBase(self, path: str, base: str) -> bool:
|
||||
try:
|
||||
return os.path.commonpath([os.path.realpath(path), os.path.realpath(base)]) == os.path.realpath(base)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def __initPeersList(self):
|
||||
self.Peers: list[Peer] = []
|
||||
self.getPeers()
|
||||
|
|
@ -1291,4 +1320,4 @@ class WireguardConfiguration:
|
|||
conn.execute(sqlalchemy.text('VACUUM;'))
|
||||
except Exception as e:
|
||||
return False
|
||||
return True
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@ export default {
|
|||
success: false,
|
||||
loading: false,
|
||||
parseInterfaceResult: undefined,
|
||||
parsePeersResult: undefined
|
||||
parsePeersResult: undefined,
|
||||
rawConfiguration: "",
|
||||
rawConfigHasPeers: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
@ -81,7 +83,11 @@ export default {
|
|||
async saveNewConfiguration(){
|
||||
if (this.goodToSubmit){
|
||||
this.loading = true;
|
||||
await fetchPost("/api/addWireguardConfiguration", this.newConfiguration, async (res) => {
|
||||
const payload = {...this.newConfiguration};
|
||||
if (this.rawConfiguration && this.rawConfigHasPeers){
|
||||
payload.RawConfiguration = this.rawConfiguration;
|
||||
}
|
||||
await fetchPost("/api/addWireguardConfiguration", payload, async (res) => {
|
||||
if (res.status){
|
||||
this.success = true
|
||||
await this.store.getConfigurations()
|
||||
|
|
@ -104,8 +110,11 @@ export default {
|
|||
if (!file) return false;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (evt) => {
|
||||
this.parseInterfaceResult = parseInterface(evt.target.result);
|
||||
this.parsePeersResult = parsePeers(evt.target.result);
|
||||
const raw = evt.target.result;
|
||||
this.rawConfiguration = raw;
|
||||
this.parseInterfaceResult = parseInterface(raw);
|
||||
this.parsePeersResult = parsePeers(raw);
|
||||
this.rawConfigHasPeers = Array.isArray(this.parsePeersResult) && this.parsePeersResult.length > 0;
|
||||
let appliedFields = 0;
|
||||
if (this.parseInterfaceResult){
|
||||
this.newConfiguration.ConfigurationName = file.name.replace('.conf', '')
|
||||
|
|
@ -118,6 +127,9 @@ export default {
|
|||
}
|
||||
if (appliedFields > 0){
|
||||
this.dashboardStore.newMessage("WGDashboard", `Parse successful! Updated ${appliedFields} field(s)`, "success")
|
||||
if (this.rawConfigHasPeers){
|
||||
this.dashboardStore.newMessage("WGDashboard", "Peers detected. The uploaded file will be imported as-is to preserve peer definitions.", "info")
|
||||
}
|
||||
}else {
|
||||
this.dashboardStore.newMessage("WGDashboard", `Parse failed`, "danger")
|
||||
}
|
||||
|
|
@ -415,4 +427,4 @@ export default {
|
|||
.protocolBtnGroup a{
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue