mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Add ssh policy info for peers (#511)
This commit is contained in:
@@ -4,7 +4,18 @@ import md5 from "crypto-js/md5";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { usePermissions } from "@/contexts/PermissionsProvider";
|
import { usePermissions } from "@/contexts/PermissionsProvider";
|
||||||
|
|
||||||
const initialAnnouncements: Announcement[] = [];
|
const initialAnnouncements: Announcement[] = [
|
||||||
|
{
|
||||||
|
tag: "New",
|
||||||
|
text: "NetBird v0.60.0 - Identity-aware, private SSH over your NetBird network.",
|
||||||
|
link: "https://docs.netbird.io/how-to/ssh",
|
||||||
|
linkText: "Documentation",
|
||||||
|
variant: "default", // "default" or "important"
|
||||||
|
isExternal: true,
|
||||||
|
closeable: true,
|
||||||
|
isCloudOnly: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export interface Announcement extends AnnouncementVariant {
|
export interface Announcement extends AnnouncementVariant {
|
||||||
tag: string;
|
tag: string;
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export default function PeerProvider({
|
|||||||
<PeerSSHInstructions
|
<PeerSSHInstructions
|
||||||
open={sshInstructionsModal}
|
open={sshInstructionsModal}
|
||||||
onOpenChange={setSSHInstructionsModal}
|
onOpenChange={setSSHInstructionsModal}
|
||||||
|
peer={peer}
|
||||||
onSuccess={() => toggleSSH(true)}
|
onSuccess={() => toggleSSH(true)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import React, { useMemo, useState } from "react";
|
|||||||
import AccessControlIcon from "@/assets/icons/AccessControlIcon";
|
import AccessControlIcon from "@/assets/icons/AccessControlIcon";
|
||||||
import { usePermissions } from "@/contexts/PermissionsProvider";
|
import { usePermissions } from "@/contexts/PermissionsProvider";
|
||||||
import { Group } from "@/interfaces/Group";
|
import { Group } from "@/interfaces/Group";
|
||||||
import { Policy, Protocol } from "@/interfaces/Policy";
|
import { Policy, PolicyRuleResource, Protocol } from "@/interfaces/Policy";
|
||||||
import { PostureCheck } from "@/interfaces/PostureCheck";
|
import { PostureCheck } from "@/interfaces/PostureCheck";
|
||||||
import { useAccessControl } from "@/modules/access-control/useAccessControl";
|
import { useAccessControl } from "@/modules/access-control/useAccessControl";
|
||||||
import { PostureCheckTab } from "@/modules/posture-checks/ui/PostureCheckTab";
|
import { PostureCheckTab } from "@/modules/posture-checks/ui/PostureCheckTab";
|
||||||
@@ -116,6 +116,9 @@ type ModalProps = {
|
|||||||
postureCheckTemplates?: PostureCheck[];
|
postureCheckTemplates?: PostureCheck[];
|
||||||
useSave?: boolean;
|
useSave?: boolean;
|
||||||
allowEditPeers?: boolean;
|
allowEditPeers?: boolean;
|
||||||
|
initialProtocol?: Protocol;
|
||||||
|
initialPorts?: number[];
|
||||||
|
initialDestinationResource?: PolicyRuleResource;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AccessControlModalContent({
|
export function AccessControlModalContent({
|
||||||
@@ -128,6 +131,9 @@ export function AccessControlModalContent({
|
|||||||
initialDestinationGroups,
|
initialDestinationGroups,
|
||||||
initialName,
|
initialName,
|
||||||
initialDescription,
|
initialDescription,
|
||||||
|
initialProtocol,
|
||||||
|
initialPorts,
|
||||||
|
initialDestinationResource,
|
||||||
}: Readonly<ModalProps>) {
|
}: Readonly<ModalProps>) {
|
||||||
const { permission } = usePermissions();
|
const { permission } = usePermissions();
|
||||||
|
|
||||||
@@ -170,6 +176,9 @@ export function AccessControlModalContent({
|
|||||||
initialDestinationGroups,
|
initialDestinationGroups,
|
||||||
initialName,
|
initialName,
|
||||||
initialDescription,
|
initialDescription,
|
||||||
|
initialPorts,
|
||||||
|
initialProtocol,
|
||||||
|
initialDestinationResource,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [tab, setTab] = useState(() => {
|
const [tab, setTab] = useState(() => {
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { useSWRConfig } from "swr";
|
import { useSWRConfig } from "swr";
|
||||||
import { usePolicies } from "@/contexts/PoliciesProvider";
|
import { usePolicies } from "@/contexts/PoliciesProvider";
|
||||||
import { Group } from "@/interfaces/Group";
|
import { Group } from "@/interfaces/Group";
|
||||||
import { Policy, PortRange, Protocol } from "@/interfaces/Policy";
|
import {
|
||||||
|
Policy,
|
||||||
|
PolicyRuleResource,
|
||||||
|
PortRange,
|
||||||
|
Protocol,
|
||||||
|
} from "@/interfaces/Policy";
|
||||||
import { PostureCheck } from "@/interfaces/PostureCheck";
|
import { PostureCheck } from "@/interfaces/PostureCheck";
|
||||||
import useGroupHelper from "@/modules/groups/useGroupHelper";
|
import useGroupHelper from "@/modules/groups/useGroupHelper";
|
||||||
import { usePostureCheck } from "@/modules/posture-checks/usePostureCheck";
|
import { usePostureCheck } from "@/modules/posture-checks/usePostureCheck";
|
||||||
@@ -18,6 +23,9 @@ type Props = {
|
|||||||
initialDestinationGroups?: Group[] | string[];
|
initialDestinationGroups?: Group[] | string[];
|
||||||
initialName?: string;
|
initialName?: string;
|
||||||
initialDescription?: string;
|
initialDescription?: string;
|
||||||
|
initialProtocol?: Protocol;
|
||||||
|
initialPorts?: number[];
|
||||||
|
initialDestinationResource?: PolicyRuleResource;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO add reducer
|
// TODO add reducer
|
||||||
@@ -29,6 +37,9 @@ export const useAccessControl = ({
|
|||||||
initialName,
|
initialName,
|
||||||
initialDescription,
|
initialDescription,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
initialProtocol,
|
||||||
|
initialPorts,
|
||||||
|
initialDestinationResource,
|
||||||
}: Props = {}) => {
|
}: Props = {}) => {
|
||||||
const { data: allPostureChecks, isLoading: isPostureChecksLoading } =
|
const { data: allPostureChecks, isLoading: isPostureChecksLoading } =
|
||||||
useFetchApi<PostureCheck[]>("/posture-checks");
|
useFetchApi<PostureCheck[]>("/posture-checks");
|
||||||
@@ -75,6 +86,7 @@ export const useAccessControl = ({
|
|||||||
const [enabled, setEnabled] = useState<boolean>(policy?.enabled ?? true);
|
const [enabled, setEnabled] = useState<boolean>(policy?.enabled ?? true);
|
||||||
|
|
||||||
const [ports, setPorts] = useState<number[]>(() => {
|
const [ports, setPorts] = useState<number[]>(() => {
|
||||||
|
if (initialPorts) return initialPorts;
|
||||||
if (!firstRule) return [];
|
if (!firstRule) return [];
|
||||||
if (firstRule.ports == undefined) return [];
|
if (firstRule.ports == undefined) return [];
|
||||||
if (firstRule.ports.length > 0) {
|
if (firstRule.ports.length > 0) {
|
||||||
@@ -93,7 +105,7 @@ export const useAccessControl = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [protocol, setProtocol] = useState<Protocol>(
|
const [protocol, setProtocol] = useState<Protocol>(
|
||||||
firstRule ? firstRule.protocol : "all",
|
firstRule ? firstRule.protocol : initialProtocol ?? "all",
|
||||||
);
|
);
|
||||||
const [direction, setDirection] = useState<Direction>(() => {
|
const [direction, setDirection] = useState<Direction>(() => {
|
||||||
if (!firstRule) return "bi";
|
if (!firstRule) return "bi";
|
||||||
@@ -131,7 +143,7 @@ export const useAccessControl = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [destinationResource, setDestinationResource] = useState(
|
const [destinationResource, setDestinationResource] = useState(
|
||||||
firstRule?.destinationResource,
|
firstRule?.destinationResource ?? initialDestinationResource,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { updateOrCreateAndNotify: checkToCreate } = usePostureCheck({});
|
const { updateOrCreateAndNotify: checkToCreate } = usePostureCheck({});
|
||||||
|
|||||||
@@ -9,26 +9,37 @@ import {
|
|||||||
} from "@components/modal/Modal";
|
} from "@components/modal/Modal";
|
||||||
import ModalHeader from "@components/modal/ModalHeader";
|
import ModalHeader from "@components/modal/ModalHeader";
|
||||||
import Paragraph from "@components/Paragraph";
|
import Paragraph from "@components/Paragraph";
|
||||||
|
import { SegmentedTabs } from "@components/SegmentedTabs";
|
||||||
import Separator from "@components/Separator";
|
import Separator from "@components/Separator";
|
||||||
import Steps from "@components/Steps";
|
import Steps from "@components/Steps";
|
||||||
import { Lightbox } from "@components/ui/Lightbox";
|
import { Lightbox } from "@components/ui/Lightbox";
|
||||||
import { Mark } from "@components/ui/Mark";
|
import { Mark } from "@components/ui/Mark";
|
||||||
import { cn } from "@utils/helpers";
|
import { cn } from "@utils/helpers";
|
||||||
import { ExternalLinkIcon, TerminalSquare } from "lucide-react";
|
import { ExternalLinkIcon, PlusCircle, TerminalSquare } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import NetBirdIcon from "@/assets/icons/NetBirdIcon";
|
||||||
import sshImage from "@/assets/ssh/ssh-client.png";
|
import sshImage from "@/assets/ssh/ssh-client.png";
|
||||||
|
import { Peer } from "@/interfaces/Peer";
|
||||||
|
import { PeerSSHPolicyModal } from "@/modules/peer/PeerSSHPolicyModal";
|
||||||
|
import { Terminal } from "@/modules/remote-access/ssh/Terminal";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
onOpenChange?: (open: boolean) => void;
|
onOpenChange?: (open: boolean) => void;
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
|
peer?: Peer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PeerSSHInstructions = ({
|
export const PeerSSHInstructions = ({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
|
peer,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const [client, setClient] = useState("cli");
|
||||||
|
const [policyModal, setPolicyModal] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open={open} onOpenChange={onOpenChange}>
|
<Modal open={open} onOpenChange={onOpenChange}>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
@@ -39,36 +50,70 @@ export const PeerSSHInstructions = ({
|
|||||||
icon={<TerminalSquare size={16} className={"text-netbird"} />}
|
icon={<TerminalSquare size={16} className={"text-netbird"} />}
|
||||||
title={"Enable SSH Access"}
|
title={"Enable SSH Access"}
|
||||||
description={
|
description={
|
||||||
"Allow remote SSH access to this machine from other connected network participants."
|
"Allow remote SSH access from other connected network participants."
|
||||||
}
|
}
|
||||||
color={"netbird"}
|
color={"netbird"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<div className={"px-8 py-3 flex flex-col gap-0 z-0"}>
|
<div className={"px-8 py-3 flex flex-col gap-0 z-0 mt-1"}>
|
||||||
|
<SegmentedTabs value={client} onChange={setClient}>
|
||||||
|
<SegmentedTabs.List className={"rounded-lg border"}>
|
||||||
|
<SegmentedTabs.Trigger value={"cli"}>
|
||||||
|
<TerminalSquare size={16} />
|
||||||
|
CLI
|
||||||
|
</SegmentedTabs.Trigger>
|
||||||
|
<SegmentedTabs.Trigger value={"gui"}>
|
||||||
|
<NetBirdIcon size={16} />
|
||||||
|
Desktop Client
|
||||||
|
</SegmentedTabs.Trigger>
|
||||||
|
</SegmentedTabs.List>
|
||||||
|
</SegmentedTabs>
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Steps.Step step={1}>
|
{client === "cli" ? (
|
||||||
<p className={"font-normal"}>
|
<Steps.Step step={1}>
|
||||||
If you are using NetBird via CLI, you can enable SSH by running
|
<p className={"font-normal"}>
|
||||||
</p>
|
If you are using NetBird via CLI, you can enable SSH by
|
||||||
<Code codeToCopy={"netbird down"}>
|
running
|
||||||
<Code.Line>{`netbird down # if NetBird is already running`}</Code.Line>
|
</p>
|
||||||
</Code>
|
<Code codeToCopy={"netbird down"}>
|
||||||
<Code>
|
<Code.Line>{`netbird down # if NetBird is already running`}</Code.Line>
|
||||||
<Code.Line>{`netbird up --allow-server-ssh --enable-ssh-root`}</Code.Line>
|
</Code>
|
||||||
</Code>
|
<Code>
|
||||||
</Steps.Step>
|
<Code.Line>{`netbird up --allow-server-ssh --enable-ssh-root`}</Code.Line>
|
||||||
|
</Code>
|
||||||
|
</Steps.Step>
|
||||||
|
) : (
|
||||||
|
<Steps.Step step={1}>
|
||||||
|
<p className={"font-normal"}>
|
||||||
|
If you are using NetBird via the Desktop Client, click on the
|
||||||
|
NetBird tray icon, go to <Mark>Settings</Mark> and click{" "}
|
||||||
|
<Mark>Allow SSH</Mark>. If you want to enable Root Login go to{" "}
|
||||||
|
<Mark>Settings > Advanced Settings</Mark> and enable SSH
|
||||||
|
Root Login under the SSH tab.
|
||||||
|
</p>
|
||||||
|
<Lightbox image={sshImage} />
|
||||||
|
</Steps.Step>
|
||||||
|
)}
|
||||||
|
|
||||||
<Steps.Step step={2}>
|
<Steps.Step step={2}>
|
||||||
<p className={"font-normal"}>
|
<p className={"font-normal"}>
|
||||||
If you are using NetBird via the Desktop Client, click on the
|
Starting from NetBird v0.60.0, SSH requires an explicit access
|
||||||
NetBird tray icon, go to <Mark>Settings</Mark> and click{" "}
|
control policy that allows <Mark>TCP</Mark> traffic on port{" "}
|
||||||
<Mark>Allow SSH</Mark> <br />
|
<Mark>22</Mark>
|
||||||
</p>
|
</p>
|
||||||
<Lightbox image={sshImage} />
|
<div className={"mt-2"}>
|
||||||
|
<Button
|
||||||
|
variant={"secondary"}
|
||||||
|
onClick={() => setPolicyModal(true)}
|
||||||
|
>
|
||||||
|
<PlusCircle size={16} />
|
||||||
|
Create SSH Policy
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Steps.Step>
|
</Steps.Step>
|
||||||
|
|
||||||
<Steps.Step step={3} line={false}>
|
<Steps.Step step={3} line={false}>
|
||||||
<p className={"font-normal"}>
|
<p className={"font-normal"}>
|
||||||
Once the NetBird SSH server is allowed on the client, <br />
|
Once the NetBird SSH server is allowed on the client, <br />
|
||||||
@@ -96,15 +141,17 @@ export const PeerSSHInstructions = ({
|
|||||||
<Button variant={"secondary"}>Cancel</Button>
|
<Button variant={"secondary"}>Cancel</Button>
|
||||||
</ModalClose>
|
</ModalClose>
|
||||||
|
|
||||||
<Button
|
<Button variant={"primary"} onClick={onSuccess}>
|
||||||
variant={"primary"}
|
|
||||||
onClick={onSuccess}
|
|
||||||
data-cy={"create-setup-key"}
|
|
||||||
>
|
|
||||||
Confirm & Enable
|
Confirm & Enable
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
||||||
|
<PeerSSHPolicyModal
|
||||||
|
open={policyModal}
|
||||||
|
onOpenChange={setPolicyModal}
|
||||||
|
peer={peer}
|
||||||
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
38
src/modules/peer/PeerSSHPolicyInfo.tsx
Normal file
38
src/modules/peer/PeerSSHPolicyInfo.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Callout } from "@components/Callout";
|
||||||
|
import { InlineButtonLink } from "@components/InlineLink";
|
||||||
|
import { cn } from "@utils/helpers";
|
||||||
|
import * as React from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Peer } from "@/interfaces/Peer";
|
||||||
|
import { PeerSSHPolicyModal } from "@/modules/peer/PeerSSHPolicyModal";
|
||||||
|
import { usePeerSSHPolicyCheck } from "@/modules/peer/usePeerSSHPolicyCheck";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
peer?: Peer;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PeerSSHPolicyInfo = ({ peer, className }: Props) => {
|
||||||
|
const { showSSHPolicyInfo } = usePeerSSHPolicyCheck(peer);
|
||||||
|
const [policyModal, setPolicyModal] = useState(false);
|
||||||
|
return (
|
||||||
|
showSSHPolicyInfo && (
|
||||||
|
<>
|
||||||
|
<Callout className={cn("max-w-xl", className)} variant={"warning"}>
|
||||||
|
<span>
|
||||||
|
Starting from NetBird v0.60.0, SSH requires an explicit access
|
||||||
|
control policy that allows TCP traffic on port 22.{" "}
|
||||||
|
<InlineButtonLink onClick={() => setPolicyModal(true)}>
|
||||||
|
Create SSH Policy
|
||||||
|
</InlineButtonLink>
|
||||||
|
</span>
|
||||||
|
</Callout>
|
||||||
|
<PeerSSHPolicyModal
|
||||||
|
open={policyModal}
|
||||||
|
onOpenChange={setPolicyModal}
|
||||||
|
peer={peer}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
35
src/modules/peer/PeerSSHPolicyModal.tsx
Normal file
35
src/modules/peer/PeerSSHPolicyModal.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Modal } from "@components/modal/Modal";
|
||||||
|
import * as React from "react";
|
||||||
|
import { Peer } from "@/interfaces/Peer";
|
||||||
|
import { PolicyRuleResource } from "@/interfaces/Policy";
|
||||||
|
import { AccessControlModalContent } from "@/modules/access-control/AccessControlModal";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
peer?: Peer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PeerSSHPolicyModal = ({ open, onOpenChange, peer }: Props) => {
|
||||||
|
return (
|
||||||
|
<Modal open={open} onOpenChange={onOpenChange}>
|
||||||
|
<AccessControlModalContent
|
||||||
|
key={open ? "1" : "0"}
|
||||||
|
initialPorts={[22]}
|
||||||
|
initialProtocol={"tcp"}
|
||||||
|
initialName={"SSH Access"}
|
||||||
|
initialDestinationResource={
|
||||||
|
peer
|
||||||
|
? ({
|
||||||
|
id: peer.id,
|
||||||
|
type: "peer",
|
||||||
|
} as PolicyRuleResource)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onSuccess={async (p) => {
|
||||||
|
onOpenChange(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import { LockIcon, TerminalSquare } from "lucide-react";
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { usePeer } from "@/contexts/PeerProvider";
|
import { usePeer } from "@/contexts/PeerProvider";
|
||||||
import { usePermissions } from "@/contexts/PermissionsProvider";
|
import { usePermissions } from "@/contexts/PermissionsProvider";
|
||||||
|
import { PeerSSHPolicyInfo } from "@/modules/peer/PeerSSHPolicyInfo";
|
||||||
|
|
||||||
export const PeerSSHToggle = () => {
|
export const PeerSSHToggle = () => {
|
||||||
const { permission } = usePermissions();
|
const { permission } = usePermissions();
|
||||||
@@ -42,6 +43,7 @@ export const PeerSSHToggle = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</FullTooltip>
|
</FullTooltip>
|
||||||
|
<PeerSSHPolicyInfo peer={peer} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
77
src/modules/peer/usePeerSSHPolicyCheck.ts
Normal file
77
src/modules/peer/usePeerSSHPolicyCheck.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import useFetchApi from "@utils/api";
|
||||||
|
import { Peer } from "@/interfaces/Peer";
|
||||||
|
import { Policy } from "@/interfaces/Policy";
|
||||||
|
import { isNativeSSHSupported } from "@utils/version";
|
||||||
|
|
||||||
|
export const usePeerSSHPolicyCheck = (peer?: Peer) => {
|
||||||
|
const { data: policies, isLoading } = useFetchApi<Policy[]>(
|
||||||
|
"/policies",
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
const peerGroupIds = peer?.groups?.map((p) => p.id);
|
||||||
|
|
||||||
|
const peerPolicies = policies?.filter((policy) => {
|
||||||
|
// Skip disabled policies
|
||||||
|
if (!policy?.enabled) return false;
|
||||||
|
|
||||||
|
const rule = policy?.rules?.[0];
|
||||||
|
if (!rule) return false;
|
||||||
|
|
||||||
|
// Skip icmp and udp
|
||||||
|
if (rule.protocol === "icmp" || rule.protocol === "udp") return false;
|
||||||
|
|
||||||
|
// Check resource and groups
|
||||||
|
const isPeerInDestinationResource =
|
||||||
|
rule.destinationResource?.id === peer?.id;
|
||||||
|
const isPeerInDestinationGroup =
|
||||||
|
rule.destinations?.some((group) => {
|
||||||
|
const groupId = typeof group === "string" ? group : group?.id;
|
||||||
|
return peerGroupIds?.includes(groupId);
|
||||||
|
}) ?? false;
|
||||||
|
|
||||||
|
const isPeerInDestination =
|
||||||
|
isPeerInDestinationResource || isPeerInDestinationGroup;
|
||||||
|
|
||||||
|
// If bidirectional, also check if peer is in source
|
||||||
|
let isPeerInSource = false;
|
||||||
|
if (rule.bidirectional) {
|
||||||
|
const isPeerInSourceResource = rule.sourceResource?.id === peer?.id;
|
||||||
|
const isPeerInSourceGroup =
|
||||||
|
rule.sources?.some((group) => {
|
||||||
|
const groupId = typeof group === "string" ? group : group?.id;
|
||||||
|
return peerGroupIds?.includes(groupId);
|
||||||
|
}) ?? false;
|
||||||
|
|
||||||
|
isPeerInSource = isPeerInSourceResource || isPeerInSourceGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInSourceOrDestination = isPeerInDestination || isPeerInSource;
|
||||||
|
if (!isInSourceOrDestination) return false;
|
||||||
|
|
||||||
|
if (rule.protocol === "all") return true;
|
||||||
|
|
||||||
|
// Check ports
|
||||||
|
const hasNoPortRestrictions = rule.ports === undefined;
|
||||||
|
const hasExplicitPort22 = rule.ports?.includes("22");
|
||||||
|
const hasPort22InRange = rule.port_ranges?.some(
|
||||||
|
(range) => 22 >= range.start && 22 <= range.end,
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasNoPortRestrictions || hasExplicitPort22 || hasPort22InRange;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasSSHPolicy = (peerPolicies?.length ?? 0) > 0;
|
||||||
|
const showSSHPolicyInfo =
|
||||||
|
!hasSSHPolicy &&
|
||||||
|
!isLoading &&
|
||||||
|
!!peer?.ssh_enabled &&
|
||||||
|
isNativeSSHSupported(peer.version);
|
||||||
|
|
||||||
|
return {
|
||||||
|
peerPolicies,
|
||||||
|
isCheckLoading: isLoading,
|
||||||
|
hasSSHPolicy,
|
||||||
|
showSSHPolicyInfo,
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user