mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-12 21:30:35 +01:00
Merge branch 'rules' of https://github.com/fosrl/pangolin into rules
This commit is contained in:
commit
5b44ffa2fb
3 changed files with 59 additions and 22 deletions
|
@ -485,18 +485,42 @@ async function checkRules(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hasAcceptRule = false;
|
||||||
|
|
||||||
|
// First pass: look for DROP rules
|
||||||
for (const rule of rules) {
|
for (const rule of rules) {
|
||||||
if (
|
if (
|
||||||
clientIp &&
|
(clientIp &&
|
||||||
rule.match == "CIDR" &&
|
rule.match == "CIDR" &&
|
||||||
isIpInCidr(clientIp, rule.value)
|
isIpInCidr(clientIp, rule.value) &&
|
||||||
|
rule.action === "DROP") ||
|
||||||
|
(path &&
|
||||||
|
rule.match == "PATH" &&
|
||||||
|
urlGlobToRegex(rule.value).test(path) &&
|
||||||
|
rule.action === "DROP")
|
||||||
) {
|
) {
|
||||||
return rule.action as "ACCEPT" | "DROP";
|
return "DROP";
|
||||||
} else if (path && rule.match == "PATH") {
|
}
|
||||||
// rule.value is a regex, match on the path and see if it matches
|
// Track if we see any ACCEPT rules for the second pass
|
||||||
const re = urlGlobToRegex(rule.value);
|
if (rule.action === "ACCEPT") {
|
||||||
if (re.test(path)) {
|
hasAcceptRule = true;
|
||||||
return rule.action as "ACCEPT" | "DROP";
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: only check ACCEPT rules if we found one and didn't find a DROP
|
||||||
|
if (hasAcceptRule) {
|
||||||
|
for (const rule of rules) {
|
||||||
|
if (rule.action !== "ACCEPT") continue;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(clientIp &&
|
||||||
|
rule.match == "CIDR" &&
|
||||||
|
isIpInCidr(clientIp, rule.value)) ||
|
||||||
|
(path &&
|
||||||
|
rule.match == "PATH" &&
|
||||||
|
urlGlobToRegex(rule.value).test(path))
|
||||||
|
) {
|
||||||
|
return "ACCEPT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,8 +529,8 @@ async function checkRules(
|
||||||
}
|
}
|
||||||
|
|
||||||
function urlGlobToRegex(pattern: string): RegExp {
|
function urlGlobToRegex(pattern: string): RegExp {
|
||||||
// Remove leading slash if present (we'll add it to the regex pattern)
|
// Trim any leading or trailing slashes
|
||||||
pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern;
|
pattern = pattern.replace(/^\/+|\/+$/g, "");
|
||||||
|
|
||||||
// Escape special regex characters except *
|
// Escape special regex characters except *
|
||||||
const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
const escapedPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
@ -516,6 +540,7 @@ function urlGlobToRegex(pattern: string): RegExp {
|
||||||
|
|
||||||
// Create the final pattern that:
|
// Create the final pattern that:
|
||||||
// 1. Optionally matches leading slash
|
// 1. Optionally matches leading slash
|
||||||
// 2. Matches the entire string
|
// 2. Matches the pattern
|
||||||
return new RegExp(`^/?${regexPattern}$`);
|
// 3. Optionally matches trailing slash
|
||||||
}
|
return new RegExp(`^/?${regexPattern}/?$`);
|
||||||
|
}
|
|
@ -269,7 +269,7 @@ export default function ReverseProxyTargets(props: {
|
||||||
>(`/resource/${params.resourceId}/target`, data);
|
>(`/resource/${params.resourceId}/target`, data);
|
||||||
target.targetId = res.data.data.targetId;
|
target.targetId = res.data.data.targetId;
|
||||||
} else if (target.updated) {
|
} else if (target.updated) {
|
||||||
const res = await api.post(
|
await api.post(
|
||||||
`/target/${target.targetId}`,
|
`/target/${target.targetId}`,
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
|
@ -290,7 +290,7 @@ export default function ReverseProxyTargets(props: {
|
||||||
for (const targetId of targetsToRemove) {
|
for (const targetId of targetsToRemove) {
|
||||||
await api.delete(`/target/${targetId}`);
|
await api.delete(`/target/${targetId}`);
|
||||||
setTargets(
|
setTargets(
|
||||||
targets.filter((target) => target.targetId !== targetId)
|
targets.filter((t) => t.targetId !== targetId)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -257,30 +257,42 @@ export default function ResourceRules(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rule.new) {
|
if (rule.new) {
|
||||||
await api.put(`/resource/${params.resourceId}/rule`, data);
|
const res = await api.put(`/resource/${params.resourceId}/rule`, data);
|
||||||
|
rule.ruleId = res.data.data.ruleId;
|
||||||
} else if (rule.updated) {
|
} else if (rule.updated) {
|
||||||
await api.post(
|
await api.post(
|
||||||
`/resource/${params.resourceId}/rule/${rule.ruleId}`,
|
`/resource/${params.resourceId}/rule/${rule.ruleId}`,
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRules([
|
||||||
|
...rules.map((r) => {
|
||||||
|
let res = {
|
||||||
|
...r,
|
||||||
|
new: false,
|
||||||
|
updated: false
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ruleId of rulesToRemove) {
|
for (const ruleId of rulesToRemove) {
|
||||||
await api.delete(
|
await api.delete(
|
||||||
`/resource/${params.resourceId}/rule/${ruleId}`
|
`/resource/${params.resourceId}/rule/${ruleId}`
|
||||||
);
|
);
|
||||||
|
setRules(
|
||||||
|
rules.filter((r) => r.ruleId !== ruleId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRules(
|
|
||||||
rules.map((rule) => ({ ...rule, new: false, updated: false }))
|
|
||||||
);
|
|
||||||
setRulesToRemove([]);
|
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Rules updated",
|
title: "Rules updated",
|
||||||
description: "Rules updated successfully"
|
description: "Rules updated successfully"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setRulesToRemove([]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast({
|
toast({
|
||||||
|
|
Loading…
Reference in a new issue