Browse Source

Added the capability for users to edit the AllowedIPs of the client

pull/759/head
xwvike 3 years ago
parent
commit
e28616f9d4
  1. 2
      src/config.js
  2. 8
      src/lib/Server.js
  3. 34
      src/lib/WireGuard.js
  4. 81
      src/www/index.html
  5. 8
      src/www/js/api.js
  6. 20
      src/www/js/app.js

2
src/config.js

@ -20,7 +20,7 @@ module.exports.WG_ALLOWED_IPS = process.env.WG_ALLOWED_IPS || '0.0.0.0/0, ::/0';
module.exports.WG_PRE_UP = process.env.WG_PRE_UP || '';
module.exports.WG_POST_UP = process.env.WG_POST_UP || `
iptables -t nat -A POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ${module.exports.WG_DEVICE} -j MASQUERADE;
iptables -t nat -A POSTROUTING -o ${module.exports.WG_DEVICE} -j MASQUERADE;
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT;

8
src/lib/Server.js

@ -161,6 +161,14 @@ module.exports = class Server {
const { address } = req.body;
return WireGuard.updateClientAddress({ clientId, address });
}))
.put('/api/wireguard/client/:clientId/allowedips', Util.promisify(async (req, res) => {
const { clientId } = req.params;
if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') {
res.end(403);
}
const { allowedIPs } = req.body;
return WireGuard.updateClientAllowedIPs({ clientId, allowedIPs });
}))
.listen(PORT, WEBUI_HOST, () => {
debug(`Listening on http://${WEBUI_HOST}:${PORT}`);

34
src/lib/WireGuard.js

@ -104,6 +104,15 @@ PostDown = ${WG_POST_DOWN}
for (const [clientId, client] of Object.entries(config.clients)) {
if (!client.enabled) continue;
let allowedIPs = '';
if(client?.allowedIPs){
for (const allowedIP of client.allowedIPs) {
allowedIPs += `,${allowedIP.address}/${allowedIP.cidr}`;
}
allowedIPs = allowedIPs.substring(1);
}else {
allowedIPs = client.address;
}
result += `
@ -111,7 +120,7 @@ PostDown = ${WG_POST_DOWN}
[Peer]
PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.address}`;
AllowedIPs = ${allowedIPs}`;
}
debug('Config saving...');
@ -140,7 +149,7 @@ AllowedIPs = ${client.address}`;
publicKey: client.publicKey,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
allowedIPs: client.allowedIPs,
allowedIPs: client?.allowedIPs?client.allowedIPs:[{type:'ipv4', address:client.address, cidr: 32}],
persistentKeepalive: null,
latestHandshakeAt: null,
@ -241,7 +250,7 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
});
if (!client) {
address = `${WG_DEFAULT_ADDRESS.replace('x', i)}/32`;
address = `${WG_DEFAULT_ADDRESS.replace('x', i)}`;
break;
}
}
@ -263,6 +272,7 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
createdAt: new Date(),
updatedAt: new Date(),
allowedIPs: [{type:'ipv4', address, cidr: 32}],
enabled: true,
};
@ -316,11 +326,27 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
if (!Util.isValidIPv4(address)) {
throw new ServerError(`Invalid Address: ${address}`, 400);
}
if(client?.allowedIPs){
let allowedIPs = client.allowedIPs
client.allowedIPs[0].address = address;
}
client.address = address;
client.updatedAt = new Date();
await this.saveConfig();
}
async updateClientAllowedIPs({ clientId, allowedIPs }) {
const client = await this.getClient({ clientId });
let ips = allowedIPs.map(item=>item.address)
ips.forEach(ip=>{
if (!Util.isValidIPv4(ip)) {
throw new ServerError(`Invalid Address: ${ip}`, 400);
}
})
client.allowedIPs = allowedIPs;
client.updatedAt = new Date();
await this.saveConfig();
}
};

81
src/www/index.html

@ -216,6 +216,21 @@
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
</div>
<!-- AllowedIPs -->
<button
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
title="Edit Allowed IPs" @click="clientEditAllowedIPs = client">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 icon icon-tabler icon-tabler-list" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 6l11 0" />
<path d="M9 12l11 0" />
<path d="M9 18l11 0" />
<path d="M5 6l0 .01" />
<path d="M5 12l0 .01" />
<path d="M5 18l0 .01" />
</svg>
</button>
<!-- Show QR-->
<button
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
@ -446,6 +461,72 @@
</div>
</div>
</div>
<!--edit allowedIPs dialog-->
<div v-if="clientEditAllowedIPs" class="fixed z-10 inset-0 overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white dark:bg-neutral-700 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg w-full"
role="dialog" aria-modal="true" aria-labelledby="modal-headline">
<div class="bg-white dark:bg-neutral-700 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-check" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M9 12l2 2l4 -4" /></svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" id="modal-headline">
Edit AllowedIPs
</h3>
<div class="mt-2 flex items-center flex-col">
<div class="w-full min-h-5 mb-2 flex flex-wrap">
<div class="bg-white tracking-wide h-[40px] border-2 border-gray-100 items-center rounded p-1 mb-2 mr-2 px-2 cursor-pointer flex w-fit" v-for="(item,index) in clientEditAllowedIPs.allowedIPs">
{{item.address+'/'+item.cidr}}
<div v-if="item.address!==clientEditAllowedIPs.address" class="inline-block border-l-2 pl-1 ml-2">
<svg @click="removeIP(index)" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-x" width="18" height="18" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" /></svg>
</div>
</div>
</div>
<div class="w-full flex items-center">
<div class="h-[40px] w-fit text-sm bg-white border-2 border-gray-100 rounded flex items-center px-2">
<input maxlength="3" v-model.number="userInputIP[0]" class="w-[30px] text-center outline-none border-none bg-transparent"/><span class="text-gray-700"></span>
<input maxlength="3" v-model.number="userInputIP[1]" class="w-[30px] text-center outline-none border-none bg-transparent"/><span class="text-gray-700"></span>
<input maxlength="3" v-model.number="userInputIP[2]" class="w-[30px] text-center outline-none border-none bg-transparent"/><span class="text-gray-700"></span>
<input maxlength="3" v-model.number="userInputIP[3]" class="w-[30px] text-center outline-none border-none bg-transparent"/><span class="text-gray-700">/</span>
<input maxlength="2" v-model.number="userInputIP[4]" class="w-[30px] text-center outline-none border-none bg-transparent"/>
</div>
<div @click="addNewIP" class="w-[40px] h-[40px] rounded bg-white border-2 border-gray-100 cursor-pointer ml-2 flex justify-center items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plus" width="18" height="18" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 5l0 14" />
<path d="M5 12l14 0" /></svg>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 dark:bg-neutral-600 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" @click="updateClientAllowedIPs(clientEditAllowedIPs); clientEditAllowedIPs = null"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 dark:bg-red-600 text-base font-medium text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
Save
</button>
<button type="button" @click="clientEditAllowedIPs = null"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
</div>
</div>
</div>
<div v-if="authenticated === false">
<h1 class="text-4xl font-medium my-16 text-gray-700 dark:text-neutral-200 text-center">WireGuard</h1>

8
src/www/js/api.js

@ -116,5 +116,13 @@ class API {
body: { address },
});
}
async updateClientAllowedIPs({ clientId, allowedIPs }) {
return this.call({
method: 'put',
path: `/wireguard/client/${clientId}/allowedIPs/`,
body: { allowedIPs },
});
}
}

20
src/www/js/app.js

@ -41,6 +41,8 @@ new Vue({
clientEditNameId: null,
clientEditAddress: null,
clientEditAddressId: null,
clientEditAllowedIPs: null,
userInputIP:[0,0,0,0,0],
qrcode: null,
currentRelease: null,
@ -243,6 +245,24 @@ new Vue({
.catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
},
updateClientAllowedIPs(client) {
this.api.updateClientAllowedIPs({ clientId: client.id, allowedIPs: client.allowedIPs })
.catch((err) => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
},
addNewIP(){
let address = this.userInputIP.slice(0,4).join('.');
let allowedIPs = this.clientEditAllowedIPs.allowedIPs;
let obj = {type:'ipv4', address, cidr: this.userInputIP[4]};
allowedIPs.push(obj);
this.clientEditAllowedIPs.allowedIPs = allowedIPs;
this.userInputIP = [0,0,0,0,0];
},
removeIP(index){
let allowedIPs = this.clientEditAllowedIPs.allowedIPs;
allowedIPs.splice(index,1);
this.clientEditAllowedIPs.allowedIPs = allowedIPs;
},
toggleTheme() {
if (this.isDark) {
localStorage.theme = 'light';

Loading…
Cancel
Save