diff --git a/frontend/src/pages/Policies.tsx b/frontend/src/pages/Policies.tsx
index 048b15e..f449e88 100644
--- a/frontend/src/pages/Policies.tsx
+++ b/frontend/src/pages/Policies.tsx
@@ -216,8 +216,8 @@ export function Policies() {
setForm({ ...form, name: event.target.value })} />
- setForm({ ...form, source: event.target.value })} />
- setForm({ ...form, destination: event.target.value })} />
+ setForm({ ...form, source: event.target.value })} placeholder="any, workload:, 172.16.0.50 or 172.16.0.0/16" />
+ setForm({ ...form, destination: event.target.value })} placeholder="any, workload:, 172.16.0.53 or 172.16.10.0/24" />
diff --git a/frontend/src/pages/PolicyDesigner.tsx b/frontend/src/pages/PolicyDesigner.tsx
index 308c95f..18a695d 100644
--- a/frontend/src/pages/PolicyDesigner.tsx
+++ b/frontend/src/pages/PolicyDesigner.tsx
@@ -11,6 +11,16 @@ type TargetOption = {
value: string;
};
+const customTargetValue = "__custom_ip_cidr__";
+
+function endpointSelectValue(value: string, targets: TargetOption[]) {
+ return targets.some((target) => target.value === value) ? value : customTargetValue;
+}
+
+function isCustomEndpoint(value: string, targets: TargetOption[]) {
+ return endpointSelectValue(value, targets) === customTargetValue;
+}
+
export function PolicyDesigner() {
const queryClient = useQueryClient();
const workloads = useQuery({ queryKey: ["workloads"], queryFn: () => api
("/vms") });
@@ -35,6 +45,7 @@ export function PolicyDesigner() {
const targets = useMemo(() => {
return [
{ label: "Any", value: "any" },
+ { label: "Custom IP/CIDR", value: customTargetValue },
...(workloads.data ?? []).map((workload) => ({ label: `VM/LXC: ${workload.name}`, value: `workload:${workload.id}` })),
...(securityGroups.data ?? []).map((group) => ({ label: `Security Group: ${group.name}`, value: `sg:${group.name}` })),
...(networks.data ?? []).map((network) => ({ label: `Network: ${network.name}`, value: `network:${network.name}` })),
@@ -115,14 +126,40 @@ export function PolicyDesigner() {
-
- setForm({ ...form, destination: event.target.value })}>
+ setForm({ ...form, destination: event.target.value === customTargetValue ? "" : event.target.value })}
+ >
{targets.map((target) => )}
+ {isCustomEndpoint(form.destination, targets) ? (
+ setForm({ ...form, destination: event.target.value.trim() })}
+ placeholder="172.16.0.53 or 172.16.10.0/24"
+ required
+ />
+ ) : null}
chooseService(event.target.value)}>