mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
Disable icmp packets over private resources
This commit is contained in:
@@ -215,7 +215,8 @@ export const siteResources = pgTable("siteResources", {
|
||||
alias: varchar("alias"),
|
||||
aliasAddress: varchar("aliasAddress"),
|
||||
tcpPortRangeString: varchar("tcpPortRangeString"),
|
||||
udpPortRangeString: varchar("udpPortRangeString")
|
||||
udpPortRangeString: varchar("udpPortRangeString"),
|
||||
disableIcmp: boolean("disableIcmp").notNull().default(false)
|
||||
});
|
||||
|
||||
export const clientSiteResources = pgTable("clientSiteResources", {
|
||||
|
||||
@@ -236,7 +236,8 @@ export const siteResources = sqliteTable("siteResources", {
|
||||
alias: text("alias"),
|
||||
aliasAddress: text("aliasAddress"),
|
||||
tcpPortRangeString: text("tcpPortRangeString"),
|
||||
udpPortRangeString: text("udpPortRangeString")
|
||||
udpPortRangeString: text("udpPortRangeString"),
|
||||
disableIcmp: integer("disableIcmp", { mode: "boolean" })
|
||||
});
|
||||
|
||||
export const clientSiteResources = sqliteTable("clientSiteResources", {
|
||||
|
||||
@@ -466,6 +466,7 @@ export function generateAliasConfig(allSiteResources: SiteResource[]): Alias[] {
|
||||
export type SubnetProxyTarget = {
|
||||
sourcePrefix: string; // must be a cidr
|
||||
destPrefix: string; // must be a cidr
|
||||
disableIcmp?: boolean;
|
||||
rewriteTo?: string; // must be a cidr
|
||||
portRange?: {
|
||||
min: number;
|
||||
@@ -504,6 +505,7 @@ export function generateSubnetProxyTargets(
|
||||
...parsePortRangeString(siteResource.tcpPortRangeString, "tcp"),
|
||||
...parsePortRangeString(siteResource.udpPortRangeString, "udp")
|
||||
];
|
||||
const disableIcmp = siteResource.disableIcmp ?? false;
|
||||
|
||||
if (siteResource.mode == "host") {
|
||||
let destination = siteResource.destination;
|
||||
@@ -515,7 +517,8 @@ export function generateSubnetProxyTargets(
|
||||
targets.push({
|
||||
sourcePrefix: clientPrefix,
|
||||
destPrefix: destination,
|
||||
portRange
|
||||
portRange,
|
||||
disableIcmp
|
||||
});
|
||||
}
|
||||
|
||||
@@ -525,14 +528,16 @@ export function generateSubnetProxyTargets(
|
||||
sourcePrefix: clientPrefix,
|
||||
destPrefix: `${siteResource.aliasAddress}/32`,
|
||||
rewriteTo: destination,
|
||||
portRange
|
||||
portRange,
|
||||
disableIcmp
|
||||
});
|
||||
}
|
||||
} else if (siteResource.mode == "cidr") {
|
||||
targets.push({
|
||||
sourcePrefix: clientPrefix,
|
||||
destPrefix: siteResource.destination,
|
||||
portRange
|
||||
portRange,
|
||||
disableIcmp
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ const createSiteResourceSchema = z
|
||||
roleIds: z.array(z.int()),
|
||||
clientIds: z.array(z.int()),
|
||||
tcpPortRangeString: portRangeStringSchema,
|
||||
udpPortRangeString: portRangeStringSchema
|
||||
udpPortRangeString: portRangeStringSchema,
|
||||
disableIcmp: z.boolean().optional()
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
@@ -158,7 +159,8 @@ export async function createSiteResource(
|
||||
roleIds,
|
||||
clientIds,
|
||||
tcpPortRangeString,
|
||||
udpPortRangeString
|
||||
udpPortRangeString,
|
||||
disableIcmp
|
||||
} = parsedBody.data;
|
||||
|
||||
// Verify the site exists and belongs to the org
|
||||
@@ -245,7 +247,8 @@ export async function createSiteResource(
|
||||
alias,
|
||||
aliasAddress,
|
||||
tcpPortRangeString,
|
||||
udpPortRangeString
|
||||
udpPortRangeString,
|
||||
disableIcmp
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ export async function listAllSiteResourcesByOrg(
|
||||
alias: siteResources.alias,
|
||||
tcpPortRangeString: siteResources.tcpPortRangeString,
|
||||
udpPortRangeString: siteResources.udpPortRangeString,
|
||||
disableIcmp: siteResources.disableIcmp,
|
||||
siteName: sites.name,
|
||||
siteNiceId: sites.niceId,
|
||||
siteAddress: sites.address
|
||||
|
||||
@@ -58,7 +58,8 @@ const updateSiteResourceSchema = z
|
||||
roleIds: z.array(z.int()),
|
||||
clientIds: z.array(z.int()),
|
||||
tcpPortRangeString: portRangeStringSchema,
|
||||
udpPortRangeString: portRangeStringSchema
|
||||
udpPortRangeString: portRangeStringSchema,
|
||||
disableIcmp: z.boolean().optional()
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
@@ -165,7 +166,8 @@ export async function updateSiteResource(
|
||||
roleIds,
|
||||
clientIds,
|
||||
tcpPortRangeString,
|
||||
udpPortRangeString
|
||||
udpPortRangeString,
|
||||
disableIcmp
|
||||
} = parsedBody.data;
|
||||
|
||||
const [site] = await db
|
||||
@@ -233,7 +235,8 @@ export async function updateSiteResource(
|
||||
enabled: enabled,
|
||||
alias: alias && alias.trim() ? alias : null,
|
||||
tcpPortRangeString: tcpPortRangeString,
|
||||
udpPortRangeString: udpPortRangeString
|
||||
udpPortRangeString: udpPortRangeString,
|
||||
disableIcmp: disableIcmp
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
@@ -357,8 +360,12 @@ export async function handleMessagingForUpdatedSiteResource(
|
||||
existingSiteResource.alias !== updatedSiteResource.alias;
|
||||
const portRangesChanged =
|
||||
existingSiteResource &&
|
||||
(existingSiteResource.tcpPortRangeString !== updatedSiteResource.tcpPortRangeString ||
|
||||
existingSiteResource.udpPortRangeString !== updatedSiteResource.udpPortRangeString);
|
||||
(existingSiteResource.tcpPortRangeString !==
|
||||
updatedSiteResource.tcpPortRangeString ||
|
||||
existingSiteResource.udpPortRangeString !==
|
||||
updatedSiteResource.udpPortRangeString ||
|
||||
existingSiteResource.disableIcmp !==
|
||||
updatedSiteResource.disableIcmp);
|
||||
|
||||
// if the existingSiteResource is undefined (new resource) we don't need to do anything here, the rebuild above handled it all
|
||||
|
||||
|
||||
@@ -69,7 +69,8 @@ export default async function ClientResourcesPage(
|
||||
siteNiceId: siteResource.siteNiceId,
|
||||
niceId: siteResource.niceId,
|
||||
tcpPortRangeString: siteResource.tcpPortRangeString || null,
|
||||
udpPortRangeString: siteResource.udpPortRangeString || null
|
||||
udpPortRangeString: siteResource.udpPortRangeString || null,
|
||||
disableIcmp: siteResource.disableIcmp || false,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -43,6 +43,7 @@ export type InternalResourceRow = {
|
||||
niceId: string;
|
||||
tcpPortRangeString: string | null;
|
||||
udpPortRangeString: string | null;
|
||||
disableIcmp: boolean;
|
||||
};
|
||||
|
||||
type ClientResourcesTableProps = {
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@app/components/ui/select";
|
||||
import { Switch } from "@app/components/ui/switch";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
@@ -179,6 +180,7 @@ export default function CreateInternalResourceDialog({
|
||||
alias: z.string().nullish(),
|
||||
tcpPortRangeString: portRangeStringSchema,
|
||||
udpPortRangeString: portRangeStringSchema,
|
||||
disableIcmp: z.boolean().optional(),
|
||||
roles: z
|
||||
.array(
|
||||
z.object({
|
||||
@@ -308,6 +310,7 @@ export default function CreateInternalResourceDialog({
|
||||
alias: "",
|
||||
tcpPortRangeString: "*",
|
||||
udpPortRangeString: "*",
|
||||
disableIcmp: false,
|
||||
roles: [],
|
||||
users: [],
|
||||
clients: []
|
||||
@@ -355,6 +358,7 @@ export default function CreateInternalResourceDialog({
|
||||
alias: "",
|
||||
tcpPortRangeString: "*",
|
||||
udpPortRangeString: "*",
|
||||
disableIcmp: false,
|
||||
roles: [],
|
||||
users: [],
|
||||
clients: []
|
||||
@@ -408,6 +412,7 @@ export default function CreateInternalResourceDialog({
|
||||
: undefined,
|
||||
tcpPortRangeString: data.tcpPortRangeString,
|
||||
udpPortRangeString: data.udpPortRangeString,
|
||||
disableIcmp: data.disableIcmp ?? false,
|
||||
roleIds: data.roles
|
||||
? data.roles.map((r) => parseInt(r.id))
|
||||
: [],
|
||||
@@ -836,7 +841,7 @@ export default function CreateInternalResourceDialog({
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{t("portRestrictions")}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-4">
|
||||
{/* TCP Ports */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -960,6 +965,31 @@ export default function CreateInternalResourceDialog({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* ICMP Toggle */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="disableIcmp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel className="min-w-10">
|
||||
ICMP
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={!field.value}
|
||||
onCheckedChange={(checked) => field.onChange(!checked)}
|
||||
/>
|
||||
</FormControl>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{field.value ? t("blocked") : t("allowed")}
|
||||
</span>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@app/components/ui/select";
|
||||
import { Switch } from "@app/components/ui/switch";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
@@ -132,6 +133,7 @@ type InternalResourceData = {
|
||||
alias?: string | null;
|
||||
tcpPortRangeString?: string | null;
|
||||
udpPortRangeString?: string | null;
|
||||
disableIcmp?: boolean;
|
||||
};
|
||||
|
||||
type EditInternalResourceDialogProps = {
|
||||
@@ -167,6 +169,7 @@ export default function EditInternalResourceDialog({
|
||||
alias: z.string().nullish(),
|
||||
tcpPortRangeString: portRangeStringSchema,
|
||||
udpPortRangeString: portRangeStringSchema,
|
||||
disableIcmp: z.boolean().optional(),
|
||||
roles: z
|
||||
.array(
|
||||
z.object({
|
||||
@@ -358,6 +361,7 @@ export default function EditInternalResourceDialog({
|
||||
alias: resource.alias ?? null,
|
||||
tcpPortRangeString: resource.tcpPortRangeString ?? "*",
|
||||
udpPortRangeString: resource.udpPortRangeString ?? "*",
|
||||
disableIcmp: resource.disableIcmp ?? false,
|
||||
roles: [],
|
||||
users: [],
|
||||
clients: []
|
||||
@@ -433,6 +437,7 @@ export default function EditInternalResourceDialog({
|
||||
: null,
|
||||
tcpPortRangeString: data.tcpPortRangeString,
|
||||
udpPortRangeString: data.udpPortRangeString,
|
||||
disableIcmp: data.disableIcmp ?? false,
|
||||
roleIds: (data.roles || []).map((r) => parseInt(r.id)),
|
||||
userIds: (data.users || []).map((u) => u.id),
|
||||
clientIds: (data.clients || []).map((c) => parseInt(c.id))
|
||||
@@ -504,6 +509,7 @@ export default function EditInternalResourceDialog({
|
||||
alias: resource.alias ?? null,
|
||||
tcpPortRangeString: resource.tcpPortRangeString ?? "*",
|
||||
udpPortRangeString: resource.udpPortRangeString ?? "*",
|
||||
disableIcmp: resource.disableIcmp ?? false,
|
||||
roles: [],
|
||||
users: [],
|
||||
clients: []
|
||||
@@ -561,6 +567,7 @@ export default function EditInternalResourceDialog({
|
||||
alias: resource.alias ?? null,
|
||||
tcpPortRangeString: resource.tcpPortRangeString ?? "*",
|
||||
udpPortRangeString: resource.udpPortRangeString ?? "*",
|
||||
disableIcmp: resource.disableIcmp ?? false,
|
||||
roles: [],
|
||||
users: [],
|
||||
clients: []
|
||||
@@ -815,7 +822,7 @@ export default function EditInternalResourceDialog({
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{t("portRestrictions")}
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-4">
|
||||
{/* TCP Ports */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -939,6 +946,31 @@ export default function EditInternalResourceDialog({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* ICMP Toggle */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="disableIcmp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel className="min-w-10">
|
||||
ICMP
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={!field.value}
|
||||
onCheckedChange={(checked) => field.onChange(!checked)}
|
||||
/>
|
||||
</FormControl>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{field.value ? t("blocked") : t("allowed")}
|
||||
</span>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user