mirror of
https://github.com/wg-easy/wg-easy.git
synced 2025-09-26 19:51:15 +08:00
Feat: Global config override (#1720)
* be able to change dns. implement global override * link donate to readme * implement global config for allowed ips * change translations, fix generation * improve docs
This commit is contained in:
@@ -102,7 +102,7 @@ Now setup a reverse proxy to be able to access the Web UI from the internet.
|
||||
|
||||
If you want to access the Web UI over HTTP, change the env var `INSECURE` to `true`. This is not recommended. Only use this for testing
|
||||
|
||||
### 3. Sponsor
|
||||
### Donate
|
||||
|
||||
Are you enjoying this project? Consider donating.
|
||||
|
||||
|
@@ -4,13 +4,13 @@ title: Auto Updates
|
||||
|
||||
## Docker Compose
|
||||
|
||||
With Docker Compose WireGuard Easy can be updated with a single command:
|
||||
With Docker Compose `wg-easy` can be updated with a single command:
|
||||
|
||||
Replace `$DIR` with the directory where your `docker-compose.yml` is located.
|
||||
|
||||
```shell
|
||||
cd $DIR
|
||||
sudo docker compose -f up -d --pull always
|
||||
sudo docker compose up -d --pull always
|
||||
```
|
||||
|
||||
## Docker Run
|
||||
@@ -27,7 +27,7 @@ And then run the `docker run -d \ ...` command from [Docker Run][docker-run] aga
|
||||
|
||||
## Podman
|
||||
|
||||
To update `wg-easy` (and every container that has auto updates enabled), you can run the following commands:
|
||||
To update `wg-easy` (and every container that has auto updates enabled), you can run the following command:
|
||||
|
||||
```shell
|
||||
sudo podman auto-update
|
||||
|
@@ -4,7 +4,7 @@ title: Docker Run
|
||||
|
||||
To setup the IPv6 Network, simply run once:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
docker network create \
|
||||
-d bridge --ipv6 \
|
||||
-d default \
|
||||
@@ -14,9 +14,9 @@ To setup the IPv6 Network, simply run once:
|
||||
|
||||
<!-- ref: major version -->
|
||||
|
||||
To automatically install & run wg-easy, simply run:
|
||||
To automatically install & run ``wg-easy, simply run:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
docker run -d \
|
||||
--net wg \
|
||||
-e INSECURE=true \
|
||||
|
68
src/app/components/Form/NullArrayField.vue
Normal file
68
src/app/components/Form/NullArrayField.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div v-if="data === null">
|
||||
{{ emptyText || $t('form.nullNoItems') }}
|
||||
</div>
|
||||
<div v-for="(item, i) in data" v-else :key="i">
|
||||
<div class="mt-1 flex flex-row gap-1">
|
||||
<input
|
||||
:value="item"
|
||||
:name="name"
|
||||
type="text"
|
||||
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400"
|
||||
@input="update($event, i)"
|
||||
/>
|
||||
<BaseButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
value="-"
|
||||
@click="del(i)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<BaseButton
|
||||
as="input"
|
||||
type="button"
|
||||
class="rounded-lg"
|
||||
:value="$t('form.add')"
|
||||
@click="add"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const data = defineModel<string[] | null>();
|
||||
defineProps<{ emptyText?: string[]; name: string }>();
|
||||
|
||||
function update(e: Event, i: number) {
|
||||
const v = (e.target as HTMLInputElement).value;
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
data.value[i] = v;
|
||||
}
|
||||
|
||||
function add() {
|
||||
if (data.value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (data.value === null) {
|
||||
data.value = [''];
|
||||
} else {
|
||||
data.value.push('');
|
||||
}
|
||||
}
|
||||
|
||||
function del(i: number) {
|
||||
if (!data.value) {
|
||||
return;
|
||||
}
|
||||
data.value.splice(i, 1);
|
||||
if (data.value.length === 0) {
|
||||
data.value = null;
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -24,7 +24,7 @@
|
||||
·
|
||||
<a
|
||||
class="hover:underline"
|
||||
href="https://github.com/sponsors/WeeJeWel"
|
||||
href="https://github.com/wg-easy/wg-easy#donate"
|
||||
target="_blank"
|
||||
>{{ $t('layout.donate') }}</a
|
||||
>
|
||||
|
@@ -27,7 +27,7 @@
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('admin.config.dnsDesc')">{{
|
||||
$t('admin.config.dns')
|
||||
$t('general.dns')
|
||||
}}</FormHeading>
|
||||
<FormArrayField v-model="data.defaultDns" name="defaultDns" />
|
||||
</FormGroup>
|
||||
|
@@ -41,21 +41,26 @@
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('client.allowedIpsDesc')">{{
|
||||
$t('general.allowedIps')
|
||||
}}</FormHeading>
|
||||
<FormArrayField v-model="data.allowedIps" name="allowedIps" />
|
||||
<FormHeading :description="$t('client.allowedIpsDesc')">
|
||||
{{ $t('general.allowedIps') }}
|
||||
</FormHeading>
|
||||
<FormNullArrayField v-model="data.allowedIps" name="allowedIps" />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('client.serverAllowedIpsDesc')">{{
|
||||
$t('client.serverAllowedIps')
|
||||
}}</FormHeading>
|
||||
<FormHeading :description="$t('client.serverAllowedIpsDesc')">
|
||||
{{ $t('client.serverAllowedIps') }}
|
||||
</FormHeading>
|
||||
<FormArrayField
|
||||
v-model="data.serverAllowedIps"
|
||||
name="serverAllowedIps"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup></FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading :description="$t('client.dnsDesc')">
|
||||
{{ $t('general.dns') }}
|
||||
</FormHeading>
|
||||
<FormNullArrayField v-model="data.dns" name="dns" />
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<FormHeading>{{ $t('form.sectionAdvanced') }}</FormHeading>
|
||||
<FormNumberField
|
||||
@@ -142,8 +147,12 @@ const _submit = useSubmit(
|
||||
method: 'post',
|
||||
},
|
||||
{
|
||||
revert: async () => {
|
||||
await navigateTo('/');
|
||||
revert: async (success) => {
|
||||
if (success) {
|
||||
await navigateTo('/');
|
||||
} else {
|
||||
await revert();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@@ -25,6 +25,7 @@
|
||||
"updatePassword": "Update Password",
|
||||
"mtu": "MTU",
|
||||
"allowedIps": "Allowed IPs",
|
||||
"dns": "DNS",
|
||||
"persistentKeepalive": "Persistent Keepalive",
|
||||
"logout": "Logout",
|
||||
"continue": "Continue",
|
||||
@@ -95,13 +96,14 @@
|
||||
"noPrivKey": "This client has no known private key. Cannot create Configuration.",
|
||||
"showQR": "Show QR Code",
|
||||
"downloadConfig": "Download Configuration",
|
||||
"allowedIpsDesc": "Which IPs will be routed through the VPN",
|
||||
"allowedIpsDesc": "Which IPs will be routed through the VPN (overrides global config)",
|
||||
"serverAllowedIpsDesc": "Which IPs the server will route to the client",
|
||||
"mtuDesc": "Sets the maximum transmission unit (packet size) for the VPN tunnel",
|
||||
"persistentKeepaliveDesc": "Sets the interval (in seconds) for keep-alive packets. 0 disables it",
|
||||
"hooks": "Hooks",
|
||||
"hooksDescription": "Hooks only work with wg-quick",
|
||||
"hooksLeaveEmpty": "Only for wg-quick. Otherwise, leave it empty"
|
||||
"hooksLeaveEmpty": "Only for wg-quick. Otherwise, leave it empty",
|
||||
"dnsDesc": "DNS server clients will use (overrides global config)"
|
||||
},
|
||||
"dialog": {
|
||||
"change": "Change",
|
||||
@@ -121,6 +123,7 @@
|
||||
"sectionGeneral": "General",
|
||||
"sectionAdvanced": "Advanced",
|
||||
"noItems": "No items",
|
||||
"nullNoItems": "No items. Using global config",
|
||||
"add": "Add"
|
||||
},
|
||||
"admin": {
|
||||
@@ -139,11 +142,10 @@
|
||||
"connection": "Connection",
|
||||
"hostDesc": "Public hostname clients will connect to (invalidates config)",
|
||||
"portDesc": "Public UDP port clients will connect to (invalidates config)",
|
||||
"allowedIpsDesc": "Allowed IPs clients will use (invalidates config)",
|
||||
"dns": "DNS",
|
||||
"dnsDesc": "DNS server clients will use (invalidates config)",
|
||||
"mtuDesc": "MTU clients will use (invalidates config)",
|
||||
"persistentKeepaliveDesc": "Interval in seconds to send keepalives to the server. 0 = disabled (invalidates config)"
|
||||
"allowedIpsDesc": "Allowed IPs clients will use (global config)",
|
||||
"dnsDesc": "DNS server clients will use (global config)",
|
||||
"mtuDesc": "MTU clients will use (only for new clients)",
|
||||
"persistentKeepaliveDesc": "Interval in seconds to send keepalives to the server. 0 = disabled (only for new clients)"
|
||||
},
|
||||
"interface": {
|
||||
"cidrSuccess": "Changed CIDR",
|
||||
|
@@ -12,11 +12,11 @@ CREATE TABLE `clients_table` (
|
||||
`public_key` text NOT NULL,
|
||||
`pre_shared_key` text NOT NULL,
|
||||
`expires_at` text,
|
||||
`allowed_ips` text NOT NULL,
|
||||
`allowed_ips` text,
|
||||
`server_allowed_ips` text NOT NULL,
|
||||
`persistent_keepalive` integer NOT NULL,
|
||||
`mtu` integer NOT NULL,
|
||||
`dns` text NOT NULL,
|
||||
`dns` text,
|
||||
`enabled` integer NOT NULL,
|
||||
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
|
||||
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "383501e4-f8de-4413-847f-a9082f6dc398",
|
||||
"id": "8c2af02b-c4bd-4880-a9ad-b38805636208",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"clients_table": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"name": "allowed_ips",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_allowed_ips": {
|
||||
@@ -134,7 +134,7 @@
|
||||
"name": "dns",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "bf316694-e2ce-4e29-bd66-ce6c0a9d3c90",
|
||||
"prevId": "383501e4-f8de-4413-847f-a9082f6dc398",
|
||||
"id": "a61263b1-9af1-4d2e-99e9-80d08127b545",
|
||||
"prevId": "8c2af02b-c4bd-4880-a9ad-b38805636208",
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"tables": {
|
||||
@@ -106,7 +106,7 @@
|
||||
"name": "allowed_ips",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"server_allowed_ips": {
|
||||
@@ -134,7 +134,7 @@
|
||||
"name": "dns",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"enabled": {
|
||||
|
@@ -5,14 +5,14 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1741335144499,
|
||||
"when": 1741355094140,
|
||||
"tag": "0000_short_skin",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1741335153054,
|
||||
"when": 1741355098159,
|
||||
"tag": "0001_classy_the_stranger",
|
||||
"breakpoints": true
|
||||
}
|
||||
|
@@ -3,6 +3,8 @@ import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
||||
|
||||
import { oneTimeLink, user } from '../../schema';
|
||||
|
||||
/** null means use value from userConfig */
|
||||
|
||||
export const client = sqliteTable('clients_table', {
|
||||
id: int().primaryKey({ autoIncrement: true }),
|
||||
userId: int('user_id')
|
||||
@@ -22,13 +24,13 @@ export const client = sqliteTable('clients_table', {
|
||||
publicKey: text('public_key').notNull(),
|
||||
preSharedKey: text('pre_shared_key').notNull(),
|
||||
expiresAt: text('expires_at'),
|
||||
allowedIps: text('allowed_ips', { mode: 'json' }).$type<string[]>().notNull(),
|
||||
allowedIps: text('allowed_ips', { mode: 'json' }).$type<string[]>(),
|
||||
serverAllowedIps: text('server_allowed_ips', { mode: 'json' })
|
||||
.$type<string[]>()
|
||||
.notNull(),
|
||||
persistentKeepalive: int('persistent_keepalive').notNull(),
|
||||
mtu: int().notNull(),
|
||||
dns: text({ mode: 'json' }).$type<string[]>().notNull(),
|
||||
dns: text({ mode: 'json' }).$type<string[]>(),
|
||||
enabled: int({ mode: 'boolean' }).notNull(),
|
||||
createdAt: text('created_at')
|
||||
.notNull()
|
||||
|
@@ -115,8 +115,6 @@ export class ClientService {
|
||||
ipv4Address,
|
||||
ipv6Address,
|
||||
mtu: clientConfig.defaultMtu,
|
||||
allowedIps: clientConfig.defaultAllowedIps,
|
||||
dns: clientConfig.defaultDns,
|
||||
persistentKeepalive: clientConfig.defaultPersistentKeepalive,
|
||||
serverAllowedIps: [],
|
||||
enabled: true,
|
||||
|
@@ -61,11 +61,11 @@ export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
|
||||
postUp: HookSchema,
|
||||
preDown: HookSchema,
|
||||
postDown: HookSchema,
|
||||
allowedIps: AllowedIpsSchema,
|
||||
allowedIps: AllowedIpsSchema.nullable(),
|
||||
serverAllowedIps: serverAllowedIps,
|
||||
mtu: MtuSchema,
|
||||
persistentKeepalive: PersistentKeepaliveSchema,
|
||||
dns: DnsSchema,
|
||||
dns: DnsSchema.nullable(),
|
||||
})
|
||||
);
|
||||
|
||||
|
@@ -59,13 +59,13 @@ PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`;
|
||||
return `[Interface]
|
||||
PrivateKey = ${client.privateKey}
|
||||
Address = ${client.ipv4Address}/${cidr4Block}, ${client.ipv6Address}/${cidr6Block}
|
||||
DNS = ${client.dns.join(', ')}
|
||||
DNS = ${(client.dns ?? userConfig.defaultDns).join(', ')}
|
||||
MTU = ${client.mtu}
|
||||
${hookLines.length ? `${hookLines.join('\n')}\n` : ''}
|
||||
[Peer]
|
||||
PublicKey = ${wgInterface.publicKey}
|
||||
PresharedKey = ${client.preSharedKey}
|
||||
AllowedIPs = ${client.allowedIps.join(', ')}
|
||||
AllowedIPs = ${(client.allowedIps ?? userConfig.defaultAllowedIps).join(', ')}
|
||||
PersistentKeepalive = ${client.persistentKeepalive}
|
||||
Endpoint = ${userConfig.host}:${userConfig.port}`;
|
||||
},
|
||||
|
Reference in New Issue
Block a user