feat: add local DNS override toggle component and action

Implements a user interface toggle component to control the override_local_dns
setting in Headscale. This allows administrators to force clients to use the
configured DNS servers instead of their local DNS configuration.

- Added ToggleOverrideLocalDns component
- Added corresponding action handler in dns-actions.ts
- Added section to the DNS overview panel
- Updated interface to reflect config changes immediately

Fixes #125
This commit is contained in:
Roberto Matias 2025-03-26 15:45:19 +01:00
parent 6f40f9cfac
commit 908039464a
4 changed files with 69 additions and 0 deletions

View File

@ -0,0 +1,36 @@
import Dialog from '~/components/Dialog';
interface Props {
isEnabled: boolean;
isDisabled: boolean;
}
export default function Modal({ isEnabled, isDisabled }: Props) {
return (
<Dialog>
<Dialog.Button
isDisabled={isDisabled}
className={isEnabled ? "text-red-500 border-red-500" : ""}
>
{isEnabled ? 'Disable' : 'Enable'} Local DNS Override
</Dialog.Button>
<Dialog.Panel isDisabled={isDisabled}>
<Dialog.Title>
{isEnabled ? 'Disable' : 'Enable'} Local DNS Override
</Dialog.Title>
<Dialog.Text>
{isEnabled
? 'Devices will no longer have their local DNS settings overridden by Headscale.'
: 'Headscale will override local DNS settings on connected devices, forcing them to use the server\'s DNS configuration.'
}
</Dialog.Text>
<input type="hidden" name="action_id" value="toggle_override_local_dns" />
<input
type="hidden"
name="new_state"
value={isEnabled ? 'disabled' : 'enabled'}
/>
</Dialog.Panel>
</Dialog>
);
}

View File

@ -33,6 +33,8 @@ export async function dnsAction({
return removeRecord(formData, context);
case 'add_record':
return addRecord(formData, context);
case 'toggle_override_local_dns':
return toggleOverrideLocalDns(formData, context);
default:
return data({ success: false }, 400);
}
@ -221,3 +223,19 @@ async function addRecord(formData: FormData, context: LoadContext) {
await hp_getIntegration()?.onConfigChange();
}
async function toggleOverrideLocalDns(formData: FormData, context: LoadContext) {
const newState = formData.get('new_state')?.toString();
if (!newState) {
return data({ success: false }, 400);
}
await context.hs.patch([
{
path: 'dns.override_local_dns',
value: newState === 'enabled',
},
]);
await hp_getIntegration()?.onConfigChange();
}

View File

@ -9,6 +9,7 @@ import ManageRecords from './components/manage-records';
import RenameTailnet from './components/rename-tailnet';
import ToggleMagic from './components/toggle-magic';
import { dnsAction } from './dns-actions';
import ToggleOverrideLocalDns from './components/toggle-override-local-dns';
// We do not want to expose every config value
export async function loader({ context }: LoaderFunctionArgs<LoadContext>) {
@ -25,6 +26,7 @@ export async function loader({ context }: LoaderFunctionArgs<LoadContext>) {
splitDns: config.dns.nameservers.split,
searchDomains: config.dns.search_domains,
extraRecords: config.dns.extra_records,
overrideLocalDns: config.dns.override_local_dns,
};
return {
@ -59,6 +61,17 @@ export default function Page() {
<RenameTailnet name={data.baseDomain} isDisabled={isDisabled} />
<ManageNS nameservers={allNs} isDisabled={isDisabled} />
<ManageRecords records={data.extraRecords} isDisabled={isDisabled} />
<div className="flex flex-col w-2/3">
<h1 className="text-2xl font-medium mb-4">Local DNS Override</h1>
<p className="mb-4">
When enabled, Headscale will override the local DNS configuration
of connected clients, forcing them to use the configured DNS servers.
</p>
<ToggleOverrideLocalDns
isEnabled={data.overrideLocalDns || false}
isDisabled={isDisabled}
/>
</div>
<ManageDomains
searchDomains={data.searchDomains}
isDisabled={isDisabled}

View File

@ -273,6 +273,8 @@ dns:
# List of DNS servers to expose to clients.
nameservers:
override_local_dns: false
global:
- 1.1.1.1
- 1.0.0.1