Browse Source

Add option to disable ipv6 (#1951)

* add option to disable ipv6

* don't add ipv6 address

* update docs
pull/1999/head
Bernd Storath 3 days ago
committed by GitHub
parent
commit
0f663df7f6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      Dockerfile
  2. 1
      Dockerfile.dev
  3. 13
      docs/content/advanced/config/optional-config.md
  4. 51
      src/server/database/sqlite.ts
  5. 16
      src/server/utils/WireGuard.ts
  6. 2
      src/server/utils/config.ts
  7. 38
      src/server/utils/wgHelper.ts

1
Dockerfile

@ -54,6 +54,7 @@ ENV PORT=51821
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV INSECURE=false ENV INSECURE=false
ENV INIT_ENABLED=false ENV INIT_ENABLED=false
ENV DISABLE_IPV6=false
LABEL org.opencontainers.image.source=https://github.com/wg-easy/wg-easy LABEL org.opencontainers.image.source=https://github.com/wg-easy/wg-easy

1
Dockerfile.dev

@ -28,6 +28,7 @@ ENV PORT=51821
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0
ENV INSECURE=true ENV INSECURE=true
ENV INIT_ENABLED=false ENV INIT_ENABLED=false
ENV DISABLE_IPV6=false
# Install Dependencies # Install Dependencies
COPY src/package.json src/pnpm-lock.yaml ./ COPY src/package.json src/pnpm-lock.yaml ./

13
docs/content/advanced/config/optional-config.md

@ -5,7 +5,18 @@ title: Optional Configuration
You can set these environment variables to configure the container. They are not required, but can be useful in some cases. You can set these environment variables to configure the container. They are not required, but can be useful in some cases.
| Env | Default | Example | Description | | Env | Default | Example | Description |
| ---------- | --------- | ----------- | ------------------------------ | | -------------- | --------- | ----------- | ---------------------------------- |
| `PORT` | `51821` | `6789` | TCP port for Web UI. | | `PORT` | `51821` | `6789` | TCP port for Web UI. |
| `HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. | | `HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. |
| `INSECURE` | `false` | `true` | If access over http is allowed | | `INSECURE` | `false` | `true` | If access over http is allowed |
| `DISABLE_IPV6` | `false` | `true` | If IPv6 support should be disabled |
/// note | IPv6 Caveats
Disabling IPv6 will disable the creation of the default IPv6 firewall rules and won't add a IPv6 address to the interface and clients.
You will however still see a IPv6 address in the Web UI, but it won't be used.
This option can be removed in the future, as more devices support IPv6.
///

51
src/server/database/sqlite.ts

@ -2,6 +2,7 @@ import { drizzle } from 'drizzle-orm/libsql';
import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator'; import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator';
import { createClient } from '@libsql/client'; import { createClient } from '@libsql/client';
import debug from 'debug'; import debug from 'debug';
import { eq } from 'drizzle-orm';
import * as schema from './schema'; import * as schema from './schema';
import { ClientService } from './repositories/client/service'; import { ClientService } from './repositories/client/service';
@ -25,6 +26,11 @@ export async function connect() {
await initialSetup(dbService); await initialSetup(dbService);
} }
if (WG_ENV.DISABLE_IPV6) {
DB_DEBUG('Warning: Disabling IPv6...');
await disableIpv6(db);
}
return dbService; return dbService;
} }
@ -108,3 +114,48 @@ async function initialSetup(db: DBServiceType) {
await db.general.setSetupStep(0); await db.general.setSetupStep(0);
} }
} }
async function disableIpv6(db: DBType) {
// This should match the initial value migration
const postUpMatch =
' ip6tables -t nat -A POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -A INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -A FORWARD -o wg0 -j ACCEPT;';
const postDownMatch =
' ip6tables -t nat -D POSTROUTING -s {{ipv6Cidr}} -o {{device}} -j MASQUERADE; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT;';
await db.transaction(async (tx) => {
const hooks = await tx.query.hooks.findFirst({
where: eq(schema.hooks.id, 'wg0'),
});
if (!hooks) {
throw new Error('Hooks not found');
}
if (hooks.postUp.includes(postUpMatch)) {
DB_DEBUG('Disabling IPv6 in Post Up hooks...');
await tx
.update(schema.hooks)
.set({
postUp: hooks.postUp.replace(postUpMatch, ''),
postDown: hooks.postDown.replace(postDownMatch, ''),
})
.where(eq(schema.hooks.id, 'wg0'))
.execute();
} else {
DB_DEBUG('IPv6 Post Up hooks already disabled, skipping...');
}
if (hooks.postDown.includes(postDownMatch)) {
DB_DEBUG('Disabling IPv6 in Post Down hooks...');
await tx
.update(schema.hooks)
.set({
postUp: hooks.postUp.replace(postUpMatch, ''),
postDown: hooks.postDown.replace(postDownMatch, ''),
})
.where(eq(schema.hooks.id, 'wg0'))
.execute();
} else {
DB_DEBUG('IPv6 Post Down hooks already disabled, skipping...');
}
});
}

16
src/server/utils/WireGuard.ts

@ -25,13 +25,21 @@ class WireGuard {
const hooks = await Database.hooks.get(); const hooks = await Database.hooks.get();
const result = []; const result = [];
result.push(wg.generateServerInterface(wgInterface, hooks)); result.push(
wg.generateServerInterface(wgInterface, hooks, {
enableIpv6: !WG_ENV.DISABLE_IPV6,
})
);
for (const client of clients) { for (const client of clients) {
if (!client.enabled) { if (!client.enabled) {
continue; continue;
} }
result.push(wg.generateServerPeer(client)); result.push(
wg.generateServerPeer(client, {
enableIpv6: !WG_ENV.DISABLE_IPV6,
})
);
} }
result.push(''); result.push('');
@ -125,7 +133,9 @@ class WireGuard {
throw new Error('Client not found'); throw new Error('Client not found');
} }
return wg.generateClientConfig(wgInterface, userConfig, client); return wg.generateClientConfig(wgInterface, userConfig, client, {
enableIpv6: !WG_ENV.DISABLE_IPV6,
});
} }
async getClientQRCodeSVG({ clientId }: { clientId: ID }) { async getClientQRCodeSVG({ clientId }: { clientId: ID }) {

2
src/server/utils/config.ts

@ -17,6 +17,8 @@ export const WG_ENV = {
INSECURE: process.env.INSECURE === 'true', INSECURE: process.env.INSECURE === 'true',
/** Port the UI is listening on */ /** Port the UI is listening on */
PORT: assertEnv('PORT'), PORT: assertEnv('PORT'),
/** If IPv6 should be disabled */
DISABLE_IPV6: process.env.DISABLE_IPV6 === 'true',
}; };
export const WG_INITIAL_ENV = { export const WG_INITIAL_ENV = {

38
src/server/utils/wgHelper.ts

@ -5,11 +5,20 @@ import type { InterfaceType } from '#db/repositories/interface/types';
import type { UserConfigType } from '#db/repositories/userConfig/types'; import type { UserConfigType } from '#db/repositories/userConfig/types';
import type { HooksType } from '#db/repositories/hooks/types'; import type { HooksType } from '#db/repositories/hooks/types';
type Options = {
enableIpv6?: boolean;
};
export const wg = { export const wg = {
generateServerPeer: (client: Omit<ClientType, 'createdAt' | 'updatedAt'>) => { generateServerPeer: (
client: Omit<ClientType, 'createdAt' | 'updatedAt'>,
options: Options = {}
) => {
const { enableIpv6 = true } = options;
const allowedIps = [ const allowedIps = [
`${client.ipv4Address}/32`, `${client.ipv4Address}/32`,
`${client.ipv6Address}/128`, ...(enableIpv6 ? [`${client.ipv6Address}/128`] : []),
...(client.serverAllowedIps ?? []), ...(client.serverAllowedIps ?? []),
]; ];
@ -25,19 +34,29 @@ PresharedKey = ${client.preSharedKey}
AllowedIPs = ${allowedIps.join(', ')}${extraLines.length ? `\n${extraLines.join('\n')}` : ''}`; AllowedIPs = ${allowedIps.join(', ')}${extraLines.length ? `\n${extraLines.join('\n')}` : ''}`;
}, },
generateServerInterface: (wgInterface: InterfaceType, hooks: HooksType) => { generateServerInterface: (
wgInterface: InterfaceType,
hooks: HooksType,
options: Options = {}
) => {
const { enableIpv6 = true } = options;
const cidr4 = parseCidr(wgInterface.ipv4Cidr); const cidr4 = parseCidr(wgInterface.ipv4Cidr);
const cidr6 = parseCidr(wgInterface.ipv6Cidr); const cidr6 = parseCidr(wgInterface.ipv6Cidr);
const ipv4Addr = stringifyIp({ number: cidr4.start + 1n, version: 4 }); const ipv4Addr = stringifyIp({ number: cidr4.start + 1n, version: 4 });
const ipv6Addr = stringifyIp({ number: cidr6.start + 1n, version: 6 }); const ipv6Addr = stringifyIp({ number: cidr6.start + 1n, version: 6 });
const address =
`${ipv4Addr}/${cidr4.prefix}` +
(enableIpv6 ? `, ${ipv6Addr}/${cidr6.prefix}` : '');
return `# Note: Do not edit this file directly. return `# Note: Do not edit this file directly.
# Your changes will be overwritten! # Your changes will be overwritten!
# Server # Server
[Interface] [Interface]
PrivateKey = ${wgInterface.privateKey} PrivateKey = ${wgInterface.privateKey}
Address = ${ipv4Addr}/${cidr4.prefix}, ${ipv6Addr}/${cidr6.prefix} Address = ${address}
ListenPort = ${wgInterface.port} ListenPort = ${wgInterface.port}
MTU = ${wgInterface.mtu} MTU = ${wgInterface.mtu}
PreUp = ${iptablesTemplate(hooks.preUp, wgInterface)} PreUp = ${iptablesTemplate(hooks.preUp, wgInterface)}
@ -49,11 +68,18 @@ PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`;
generateClientConfig: ( generateClientConfig: (
wgInterface: InterfaceType, wgInterface: InterfaceType,
userConfig: UserConfigType, userConfig: UserConfigType,
client: ClientType client: ClientType,
options: Options = {}
) => { ) => {
const { enableIpv6 = true } = options;
const cidr4Block = parseCidr(wgInterface.ipv4Cidr).prefix; const cidr4Block = parseCidr(wgInterface.ipv4Cidr).prefix;
const cidr6Block = parseCidr(wgInterface.ipv6Cidr).prefix; const cidr6Block = parseCidr(wgInterface.ipv6Cidr).prefix;
const address =
`${client.ipv4Address}/${cidr4Block}` +
(enableIpv6 ? `, ${client.ipv6Address}/${cidr6Block}` : '');
const hookLines = [ const hookLines = [
client.preUp ? `PreUp = ${client.preUp}` : null, client.preUp ? `PreUp = ${client.preUp}` : null,
client.postUp ? `PostUp = ${client.postUp}` : null, client.postUp ? `PostUp = ${client.postUp}` : null,
@ -63,7 +89,7 @@ PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`;
return `[Interface] return `[Interface]
PrivateKey = ${client.privateKey} PrivateKey = ${client.privateKey}
Address = ${client.ipv4Address}/${cidr4Block}, ${client.ipv6Address}/${cidr6Block} Address = ${address}
DNS = ${(client.dns ?? userConfig.defaultDns).join(', ')} DNS = ${(client.dns ?? userConfig.defaultDns).join(', ')}
MTU = ${client.mtu} MTU = ${client.mtu}
${hookLines.length ? `${hookLines.join('\n')}\n` : ''} ${hookLines.length ? `${hookLines.join('\n')}\n` : ''}

Loading…
Cancel
Save