dashboardwireguard-vpn-setupwireguard-vpnwireguard-tunnelwireguard-dashboardwireguardwg-managervpnsite-to-siteobfuscation
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
8.1 KiB
261 lines
8.1 KiB
import {Component, Input, OnInit, ViewChild, ViewEncapsulation} from '@angular/core';
|
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
|
import { IPValidator } from '../../../validators/ip-address.validator';
|
|
import { NumberValidator } from '../../../validators/number.validator';
|
|
import { Server } from '../../../interfaces/server';
|
|
import { ServerService } from '../../../services/server.service';
|
|
import { DataService } from '../../../services/data.service';
|
|
import Parser, {Section} from "@jedmao/ini-parser";
|
|
import {Peer} from "../../../interfaces/peer";
|
|
import {forkJoin, from} from "rxjs";
|
|
import {map, mergeMap} from "rxjs/operators";
|
|
import {NotifierService} from "angular-notifier";
|
|
import {MatCheckboxChange} from "@angular/material/checkbox";
|
|
@Component({
|
|
selector: 'app-add-server',
|
|
templateUrl: './add-server.component.html',
|
|
styleUrls: ['./add-server.component.scss', '../dashboard.component.css'],
|
|
})
|
|
export class AddServerComponent implements OnInit {
|
|
|
|
@Input() servers: Server[];
|
|
|
|
// Translates from wg configuration keywords to form and backend keywords
|
|
wgConfTranslation = {
|
|
"Address": "address",
|
|
"PrivateKey": "private_key",
|
|
"ListenPort": "listen_port",
|
|
"PostUp": "post_up",
|
|
"PostDown": "post_down",
|
|
"PublicKey": "public_key",
|
|
|
|
// Peer
|
|
"Endpoint": "endpoint",
|
|
"AllowedIPs": "allowed_ips",
|
|
"DNS": "dns"
|
|
}
|
|
|
|
v4Subnets = [];
|
|
v6Subnets = [];
|
|
defaultListenPort = "51820"
|
|
defaultInterface = "wg0"
|
|
defaultIPv4Subnet = 24;
|
|
defaultIPv6Subnet = 64;
|
|
defaultIPv4Address = "10.0.200.1"
|
|
defaultDNS = this.defaultIPv4Address + ",8.8.8.8"
|
|
defaultIPv6Address = "fd42:42:42::1"
|
|
defaultAllowedIPs = "0.0.0.0/0, ::/0"
|
|
defaultPersistentKeepalive = 0;
|
|
|
|
|
|
serverForm: FormGroup = null;
|
|
isEdit = false;
|
|
editServer: Server = null;
|
|
|
|
initForm(){
|
|
this.serverForm = new FormGroup({
|
|
address: new FormControl(this.defaultIPv4Address, [Validators.required, IPValidator.isIPAddress]),
|
|
v6_address: new FormControl(this.defaultIPv6Address, [Validators.required, IPValidator.isIPAddress]),
|
|
subnet: new FormControl(this.defaultIPv4Subnet, [Validators.required, Validators.min(1), Validators.max(32)]),
|
|
v6_subnet: new FormControl(this.defaultIPv6Subnet, [Validators.required, Validators.min(1), Validators.max(64)]),
|
|
interface: new FormControl(this.defaultInterface, [Validators.required, Validators.minLength(3)]),
|
|
listen_port: new FormControl(this.defaultListenPort, [Validators.required, NumberValidator.stringIsNumber]),
|
|
endpoint: new FormControl('', Validators.required),
|
|
dns: new FormControl(this.defaultDNS),
|
|
allowed_ips: new FormControl(this.defaultAllowedIPs),
|
|
keep_alive: new FormControl(this.defaultPersistentKeepalive),
|
|
private_key: new FormControl('' ),
|
|
public_key: new FormControl('' ),
|
|
post_up: new FormControl(''),
|
|
post_down: new FormControl(''),
|
|
read_only: new FormControl(0),
|
|
|
|
// Unused on backend
|
|
configuration: new FormControl(''),
|
|
is_running: new FormControl(false),
|
|
peers: new FormControl([]),
|
|
});
|
|
}
|
|
|
|
ipv6SupportChanged($event: MatCheckboxChange){
|
|
let v6AddressControl = this.serverForm.get("v6_address");
|
|
let v6SubnetControl = this.serverForm.get("v6_subnet");
|
|
if($event.checked){
|
|
v6AddressControl.enable()
|
|
v6SubnetControl.enable()
|
|
}else {
|
|
v6AddressControl.disable()
|
|
v6SubnetControl.disable()
|
|
}
|
|
}
|
|
|
|
constructor(private serverAPI: ServerService, private comm: DataService, private notify: NotifierService) {
|
|
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.v4Subnets = Array(32).fill(1).map((x,i)=>i+1);
|
|
this.v6Subnets = Array(64).fill(1).map((x,i)=>i+1);
|
|
this.initForm();
|
|
|
|
this.comm.on('server-edit').subscribe((data: Server) => {
|
|
this.isEdit = true;
|
|
this.serverForm.patchValue(data);
|
|
|
|
this.editServer = data;
|
|
});
|
|
|
|
}
|
|
|
|
parseFiles($event){
|
|
const files: File[] = $event.target.files;
|
|
|
|
let observables = []
|
|
Array.from(files).forEach( (file: File) => {
|
|
|
|
let obs = from([file])
|
|
.pipe(map(x => from(x.text())))
|
|
.pipe(mergeMap(x => x))
|
|
.pipe(map((x) => new Parser().parse(x).items))
|
|
.pipe(map((x: Section[]) => x.filter(y => y.name !== "" && y.name != null)))
|
|
.pipe(map( (x: Section[]) => {
|
|
|
|
let data: any = {}
|
|
// Store filename
|
|
data.fileName = file.name;
|
|
|
|
// Convert nodes to key-value dict
|
|
x.forEach( (y: any) => {
|
|
y.nodes = y.nodes.reduce((result, filter) => {
|
|
result[this.wgConfTranslation[filter["key"]]] = filter["value"];
|
|
return result;
|
|
},{});
|
|
})
|
|
data.sections = x;
|
|
|
|
// Look for endpoint in peer configuration. TODO - Better way?
|
|
data.isClient = data.sections
|
|
.filter( section => Object.keys(section.nodes).find( nk => nk === "endpoint"))
|
|
.length > 0;
|
|
|
|
// 'Detect' if its a client
|
|
return data
|
|
}));
|
|
observables.push(obs);
|
|
});
|
|
|
|
forkJoin(observables).subscribe(data => {
|
|
let server: any = data.filter((x: any) => !x.isClient);
|
|
|
|
if(server.length !== 1) {
|
|
// TODO output error - should only be one server
|
|
this.notify.notify("error", "Detected multiple server files!")
|
|
return false;
|
|
}
|
|
server = server[0];
|
|
|
|
const peers = data.filter((x: any) => x.isClient);
|
|
//console.log(peers)
|
|
this.importProcessServer(server);
|
|
peers.forEach( peer => {
|
|
this.importProcessPeer(peer);
|
|
})
|
|
})
|
|
|
|
}
|
|
|
|
importProcessServer(server) {
|
|
let iFace: any = server.sections.find(x => x.name == "Interface")
|
|
const sPeers = server.sections.filter(x => x.name == "Peer");
|
|
|
|
if(iFace === null){
|
|
// TODO error out - should have [interface] on server
|
|
this.notify.notify("error", "Could not find [Interface] section")
|
|
return false;
|
|
}
|
|
|
|
iFace.nodes["subnet"] = iFace.nodes["address"].split("/")[1];
|
|
iFace.nodes["address"] = iFace.nodes["address"].split("/")[0];
|
|
|
|
iFace.nodes["peers"] = sPeers
|
|
.map( x => x.nodes)
|
|
.map( x => {
|
|
x.server_id = -1;
|
|
x.address = x.allowed_ips.split("/")[0]; // Allowed_ips in server is the address of the peer (Seen from server perspective)
|
|
x.allowed_ips = null; // This should be retrieved from peer data config
|
|
return x;
|
|
})
|
|
this.serverForm.patchValue({
|
|
interface: server.fileName.replace(".conf", "")
|
|
})
|
|
this.serverForm.patchValue(iFace.nodes)
|
|
|
|
|
|
}
|
|
|
|
importProcessPeer(peer){
|
|
let formPeers = this.serverForm.controls.peers.value;
|
|
let iFace: any = peer.sections.find(x => x.name == "Interface")
|
|
const sPeers = peer.sections.filter(x => x.name == "Peer");
|
|
|
|
if(sPeers.length > 1) {
|
|
// TODO not supported for multi-server peers
|
|
this.notify.notify("error", "Multi-server peers are not supported! Peer " + peer.fileName +
|
|
" will be imported partially.")
|
|
|
|
return false;
|
|
}
|
|
let sPeer = sPeers[0];
|
|
|
|
let formPeer = formPeers
|
|
.find(x => x.address.split("/")[0] === iFace.nodes.address.split("/")[0])
|
|
formPeer.name = peer.fileName;
|
|
formPeer.private_key = iFace.nodes.private_key;
|
|
formPeer.allowed_ips = sPeer.nodes.allowed_ips;
|
|
formPeer.dns = iFace.nodes.dns;
|
|
this.serverForm.patchValue({
|
|
endpoint: sPeer.nodes.endpoint.split(":")[0],
|
|
public_key: sPeer.nodes.public_key
|
|
|
|
})
|
|
}
|
|
|
|
|
|
|
|
add(form: Server) {
|
|
|
|
if (this.isEdit) {
|
|
const idx = this.servers.indexOf(this.editServer);
|
|
this.serverAPI.editServer(this.editServer, form).subscribe((server: Server) => {
|
|
this.servers[idx] = server;
|
|
this.resetForm();
|
|
});
|
|
|
|
} else {
|
|
|
|
this.serverAPI.addServer(form).subscribe((server: Server) => {
|
|
this.servers.push(server);
|
|
this.resetForm();
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
getKeyPair() {
|
|
this.serverAPI.getKeyPair().subscribe((kp: any) => {
|
|
this.serverForm.patchValue({
|
|
private_key: kp.private_key,
|
|
public_key: kp.public_key,
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
resetForm() {
|
|
this.isEdit = false;
|
|
this.editServer = null;
|
|
this.serverForm.clearValidators();
|
|
this.initForm()
|
|
}
|
|
}
|
|
|