Browse Source

validate firewall IPs

pull/2418/head
Ian Foster 6 months ago
parent
commit
080ee8a203
  1. 2
      src/app/pages/admin/interface.vue
  2. 3
      src/app/stores/global.ts
  3. 3
      src/i18n/locales/en.json
  4. 2
      src/server/database/repositories/client/types.ts
  5. 36
      src/server/utils/types.ts

2
src/app/pages/admin/interface.vue

@ -185,7 +185,7 @@ const _submit = useSubmit(
await revert();
if (success) {
// Refresh global store information after successful save
await refreshNuxtData('/api/information');
await globalStore.refreshInformation();
}
},
}

3
src/app/stores/global.ts

@ -1,5 +1,5 @@
export const useGlobalStore = defineStore('Global', () => {
const { data: information } = useFetch('/api/information', {
const { data: information, refresh: refreshInformation } = useFetch('/api/information', {
method: 'get',
});
@ -22,6 +22,7 @@ export const useGlobalStore = defineStore('Global', () => {
return {
sortClient,
information,
refreshInformation,
uiShowCharts,
toggleCharts,
uiChartType,

3
src/i18n/locales/en.json

@ -202,7 +202,8 @@
"address4": "IPv4 Address",
"address6": "IPv6 Address",
"serverAllowedIps": "Server Allowed IPs",
"firewallIps": "Firewall Allowed IPs"
"firewallIps": "Firewall Allowed IPs",
"firewallIpsInvalid": "Invalid firewall IP entry. Must be: IP (10.0.0.1), CIDR (10.0.0.0/24), IP:port (10.0.0.1:443), or IP:port/protocol (10.0.0.1:443/tcp)"
},
"user": {
"username": "Username",

2
src/server/database/repositories/client/types.ts

@ -71,7 +71,7 @@ export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
postDown: HookSchema,
allowedIps: AllowedIpsSchema.nullable(),
serverAllowedIps: serverAllowedIps,
firewallIps: AllowedIpsSchema.nullable(),
firewallIps: FirewallIpsSchema.nullable(),
mtu: MtuSchema,
jC: JcSchema,
jMin: JminSchema,

36
src/server/utils/types.ts

@ -1,6 +1,8 @@
import type { ZodSchema } from 'zod';
import z from 'zod';
import type { H3Event, EventHandlerRequest } from 'h3';
import { isIP } from 'is-ip';
import { default as isCidr } from 'is-cidr';
export type ID = number;
@ -91,6 +93,40 @@ export const AllowedIpsSchema = z
.array(AddressSchema, { message: t('zod.allowedIps') })
.min(1, { message: t('zod.allowedIps') });
// Validation for firewall IP entries
const FirewallIpEntrySchema = z
.string({ message: t('zod.client.firewallIps') })
.min(1, { message: t('zod.client.firewallIps') })
.refine(
(entry) => {
// Remove protocol suffix if present (/tcp, /udp)
const entryWithoutProto = entry.replace(/\/(tcp|udp)$/i, '');
// Check if it's IP:port format
const portMatch = entryWithoutProto.match(/^(.+):(\d+)$/);
if (portMatch) {
const [, ipPart, portPart] = portMatch;
const port = parseInt(portPart, 10);
// Remove IPv6 brackets if present
const cleanIp = ipPart.replace(/^\[|\]$/g, '');
// Validate IP and port
return (isIP(cleanIp) || isCidr(cleanIp)) && port >= 1 && port <= 65535;
}
// Check if it's just IP or CIDR
return isIP(entryWithoutProto) || isCidr(entryWithoutProto);
},
{
message: t('zod.client.firewallIpsInvalid'),
}
);
export const FirewallIpsSchema = z.array(FirewallIpEntrySchema, {
message: t('zod.client.firewallIps'),
});
export const FileSchema = z.object({
file: z.string({ message: t('zod.file') }),
});

Loading…
Cancel
Save