Skip to content

Commit 21abbfa

Browse files
committed
Add necessary changes for IPv6 support
1 parent 02a0b71 commit 21abbfa

File tree

13 files changed

+223
-107
lines changed

13 files changed

+223
-107
lines changed

src/app/(dashboard)/peer/page.tsx

Lines changed: 147 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import PageContainer from "@/layouts/PageContainer";
5858
import useGroupHelper from "@/modules/groups/useGroupHelper";
5959
import AddRouteDropdownButton from "@/modules/peer/AddRouteDropdownButton";
6060
import PeerRoutesTable from "@/modules/peer/PeerRoutesTable";
61+
import {SelectDropdown} from "@components/select/SelectDropdown";
6162

6263
export default function PeerPage() {
6364
const queryParameter = useSearchParams();
@@ -82,6 +83,9 @@ function PeerOverview() {
8283
const [loginExpiration, setLoginExpiration] = useState(
8384
peer.login_expiration_enabled,
8485
);
86+
const [ipv6Enabled, setIpv6Enabled] = useState(
87+
peer.ipv6_enabled,
88+
);
8589
const [selectedGroups, setSelectedGroups, { getAllGroupCalls }] =
8690
useGroupHelper({
8791
initial: peerGroups,
@@ -104,10 +108,11 @@ function PeerOverview() {
104108
ssh,
105109
selectedGroups,
106110
loginExpiration,
111+
ipv6Enabled
107112
]);
108113

109114
const updatePeer = async () => {
110-
const updateRequest = update(name, ssh, loginExpiration);
115+
const updateRequest = update(name, ssh, loginExpiration, ipv6Enabled);
111116
const groupCalls = getAllGroupCalls();
112117
const batchCall = groupCalls
113118
? [...groupCalls, updateRequest]
@@ -118,7 +123,7 @@ function PeerOverview() {
118123
promise: Promise.all(batchCall).then(() => {
119124
mutate("/peers/" + peer.id);
120125
mutate("/groups");
121-
updateHasChangedRef([name, ssh, selectedGroups, loginExpiration]);
126+
updateHasChangedRef([name, ssh, selectedGroups, loginExpiration, ipv6Enabled]);
122127
}),
123128
loadingMessage: "Saving the peer...",
124129
});
@@ -130,49 +135,49 @@ function PeerOverview() {
130135
<div className={"p-default py-6 mb-4"}>
131136
<Breadcrumbs>
132137
<Breadcrumbs.Item
133-
href={"/peers"}
134-
label={"Peers"}
135-
icon={<PeerIcon size={13} />}
138+
href={"/peers"}
139+
label={"Peers"}
140+
icon={<PeerIcon size={13}/>}
136141
/>
137-
<Breadcrumbs.Item label={peer.ip} active />
142+
<Breadcrumbs.Item label={peer.ip} active/>
138143
</Breadcrumbs>
139144

140145
<div className={"flex justify-between max-w-6xl items-start"}>
141146
<div>
142147
<div className={"flex items-center gap-3"}>
143148
<h1 className={"flex items-center gap-3"}>
144149
<CircleIcon
145-
active={peer.connected}
146-
size={12}
147-
className={"mb-[3px] shrink-0"}
150+
active={peer.connected}
151+
size={12}
152+
className={"mb-[3px] shrink-0"}
148153
/>
149-
<TextWithTooltip text={name} maxChars={30} />
154+
<TextWithTooltip text={name} maxChars={30}/>
150155

151156
<Modal
152-
open={showEditNameModal}
153-
onOpenChange={setShowEditNameModal}
157+
open={showEditNameModal}
158+
onOpenChange={setShowEditNameModal}
154159
>
155160
<ModalTrigger>
156161
<div
157-
className={
158-
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
159-
}
162+
className={
163+
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
164+
}
160165
>
161-
<PencilIcon size={16} />
166+
<PencilIcon size={16}/>
162167
</div>
163168
</ModalTrigger>
164169
<EditNameModal
165-
onSuccess={(newName) => {
166-
setName(newName);
167-
setShowEditNameModal(false);
168-
}}
169-
peer={peer}
170-
initialName={name}
171-
key={showEditNameModal ? 1 : 0}
170+
onSuccess={(newName) => {
171+
setName(newName);
172+
setShowEditNameModal(false);
173+
}}
174+
peer={peer}
175+
initialName={name}
176+
key={showEditNameModal ? 1 : 0}
172177
/>
173178
</Modal>
174179
</h1>
175-
<LoginExpiredBadge loginExpired={peer.login_expired} />
180+
<LoginExpiredBadge loginExpired={peer.login_expired}/>
176181
</div>
177182
<div className={"flex items-center gap-8"}>
178183
<Paragraph className={"flex items-center"}>
@@ -182,138 +187,181 @@ function PeerOverview() {
182187
</div>
183188
<div className={"flex gap-4"}>
184189
<Button
185-
variant={"default"}
186-
className={"w-full"}
187-
onClick={() => router.push("/peers")}
190+
variant={"default"}
191+
className={"w-full"}
192+
onClick={() => router.push("/peers")}
188193
>
189194
Cancel
190195
</Button>
191196
<Button
192-
variant={"primary"}
193-
className={"w-full"}
194-
onClick={() => updatePeer()}
195-
disabled={!hasChanges}
197+
variant={"primary"}
198+
className={"w-full"}
199+
onClick={() => updatePeer()}
200+
disabled={!hasChanges}
196201
>
197202
Save Changes
198203
</Button>
199204
</div>
200205
</div>
201206

202207
<div className={"flex gap-10 w-full mt-5 max-w-6xl"}>
203-
<PeerInformationCard peer={peer} />
208+
<PeerInformationCard peer={peer}/>
204209

205210
<div className={"flex flex-col gap-6 w-1/2"}>
206211
<FullTooltip
207-
content={
208-
<div
209-
className={
210-
"flex gap-2 items-center !text-nb-gray-300 text-xs"
211-
}
212-
>
213-
<IconInfoCircle size={14} />
214-
<span>
212+
content={
213+
<div
214+
className={
215+
"flex gap-2 items-center !text-nb-gray-300 text-xs"
216+
}
217+
>
218+
<IconInfoCircle size={14}/>
219+
<span>
215220
Login expiration is disabled for all peers added with an
216221
setup-key.
217222
</span>
218-
</div>
219-
}
220-
className={"w-full block"}
221-
disabled={!!peer.user_id}
223+
</div>
224+
}
225+
className={"w-full block"}
226+
disabled={!!peer.user_id}
222227
>
223228
<FancyToggleSwitch
224-
disabled={!peer.user_id}
225-
value={loginExpiration}
226-
onChange={setLoginExpiration}
229+
disabled={!peer.user_id}
230+
value={loginExpiration}
231+
onChange={setLoginExpiration}
232+
label={
233+
<>
234+
<IconCloudLock size={16}/>
235+
Login Expiration
236+
</>
237+
}
238+
helpText={
239+
"Enable to require SSO login peers to re-authenticate when their login expires."
240+
}
241+
/>
242+
</FullTooltip>
243+
<FancyToggleSwitch
244+
value={ssh}
245+
onChange={(set) =>
246+
!set
247+
? setSsh(false)
248+
: openSSHDialog().then((confirm) => setSsh(confirm))
249+
}
227250
label={
228251
<>
229-
<IconCloudLock size={16} />
230-
Login Expiration
252+
<TerminalSquare size={16}/>
253+
SSH Access
231254
</>
232255
}
233256
helpText={
234-
"Enable to require SSO login peers to re-authenticate when their login expires."
257+
"Enable the SSH server on this peer to access the machine via an secure shell."
235258
}
236-
/>
237-
</FullTooltip>
238-
<FancyToggleSwitch
239-
value={ssh}
240-
onChange={(set) =>
241-
!set
242-
? setSsh(false)
243-
: openSSHDialog().then((confirm) => setSsh(confirm))
244-
}
245-
label={
246-
<>
247-
<TerminalSquare size={16} />
248-
SSH Access
249-
</>
250-
}
251-
helpText={
252-
"Enable the SSH server on this peer to access the machine via an secure shell."
253-
}
254259
/>
255260
<div>
256261
<Label>Assigned Groups</Label>
257262
<HelpText>
258263
Use groups to control what this peer can access.
259264
</HelpText>
260265
<PeerGroupSelector
261-
onChange={setSelectedGroups}
262-
values={selectedGroups}
263-
peer={peer}
266+
onChange={setSelectedGroups}
267+
values={selectedGroups}
268+
peer={peer}
264269
/>
265270
</div>
271+
<div>
272+
<Label>IPv6 Support</Label>
273+
<HelpText>
274+
Whether to enable IPv6, disable it, or enable IPv6 if at least one group has it enabled.
275+
</HelpText>
276+
<FullTooltip
277+
content={
278+
<div
279+
className={
280+
"flex gap-2 items-center !text-nb-gray-300 text-xs"
281+
}
282+
>
283+
<IconInfoCircle size={14}/>
284+
<span>
285+
IPv6 Support requires a recent version of the NetBird client as well as a supported OS (Linux with nftables).
286+
</span>
287+
</div>
288+
}
289+
className={"w-full block"}
290+
disabled={peer.ipv6_supported}
291+
>
292+
<SelectDropdown
293+
disabled={!peer.ipv6_supported}
294+
value={ipv6Enabled}
295+
onChange={setIpv6Enabled}
296+
options={[
297+
{label: "Force enabled", value: "enabled"},
298+
{label: "Inherit from Groups", value: "inherit"},
299+
{label: "Force disabled", value: "disabled"},
300+
]}
301+
/>
302+
</FullTooltip>
303+
</div>
266304
</div>
267305
</div>
268306
</div>
269307

270-
<Separator />
308+
<Separator/>
271309

272310
{isLinux ? (
273-
<div className={"px-8 py-6"}>
274-
<div className={"max-w-6xl"}>
275-
<div className={"flex justify-between items-center"}>
276-
<div>
277-
<h2>Network Routes</h2>
278-
<Paragraph>
279-
Access other networks without installing NetBird on every
280-
resource.
281-
</Paragraph>
282-
</div>
283-
<div className={"inline-flex gap-4 justify-end"}>
311+
<div className={"px-8 py-6"}>
312+
<div className={"max-w-6xl"}>
313+
<div className={"flex justify-between items-center"}>
284314
<div>
285-
<AddRouteDropdownButton />
315+
<h2>Network Routes</h2>
316+
<Paragraph>
317+
Access other networks without installing NetBird on every
318+
resource.
319+
</Paragraph>
320+
</div>
321+
<div className={"inline-flex gap-4 justify-end"}>
322+
<div>
323+
<AddRouteDropdownButton/>
324+
</div>
286325
</div>
287326
</div>
327+
<PeerRoutesTable peer={peer}/>
288328
</div>
289-
<PeerRoutesTable peer={peer} />
290329
</div>
291-
</div>
292330
) : null}
293331
</RoutesProvider>
294332
</PageContainer>
295333
);
296334
}
297335

298-
function PeerInformationCard({ peer }: { peer: Peer }) {
299-
const { isLoading, getRegionByPeer } = useCountries();
336+
function PeerInformationCard({peer}: { peer: Peer }) {
337+
const {isLoading, getRegionByPeer} = useCountries();
300338

301339
const countryText = useMemo(() => {
302340
return getRegionByPeer(peer);
303341
}, [getRegionByPeer, peer]);
304342

305343
return (
306-
<Card>
307-
<Card.List>
308-
<Card.ListItem
309-
label={
310-
<>
311-
<MapPin size={16} />
312-
NetBird IP-Address
313-
</>
314-
}
315-
value={peer.ip}
316-
/>
344+
<Card>
345+
<Card.List>
346+
<Card.ListItem
347+
label={
348+
<>
349+
<MapPin size={16}/>
350+
NetBird IPv4-Address
351+
</>
352+
}
353+
value={peer.ip}
354+
/>
355+
356+
<Card.ListItem
357+
label={
358+
<>
359+
<MapPin size={16}/>
360+
NetBird IPv6-Address
361+
</>
362+
}
363+
value={peer.ip6}
364+
/>
317365

318366
<Card.ListItem
319367
label={

src/components/PeerGroupSelector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,16 @@ export function PeerGroupSelector({
7272
if (!group && !option) {
7373
setDropdownOptions((previous) => [
7474
...previous,
75-
{ name: name, peers: groupPeers },
75+
{ name: name, peers: groupPeers, ipv6_enabled: false },
7676
]);
7777
}
7878

7979
if (max == 1 && values.length == 1) {
80-
onChange([{ name: name, id: group?.id, peers: groupPeers }]);
80+
onChange([{ name: name, id: group?.id, peers: groupPeers, ipv6_enabled: group == null ? false : group.ipv6_enabled }]);
8181
} else {
8282
onChange((previous) => [
8383
...previous,
84-
{ name: name, id: group?.id, peers: groupPeers },
84+
{ name: name, id: group?.id, peers: groupPeers, ipv6_enabled: group == null ? false : group.ipv6_enabled },
8585
]);
8686
}
8787

0 commit comments

Comments
 (0)