mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-06-05 21:03:40 +00:00
nixos/netfoil: init
This commit is contained in:
@@ -1306,6 +1306,7 @@
|
||||
./services/networking/netbird.nix
|
||||
./services/networking/netbird/server.nix
|
||||
./services/networking/netclient.nix
|
||||
./services/networking/netfoil.nix
|
||||
./services/networking/networkd-dispatcher.nix
|
||||
./services/networking/networkmanager.nix
|
||||
./services/networking/newt.nix
|
||||
|
||||
263
nixos/modules/services/networking/netfoil.nix
Normal file
263
nixos/modules/services/networking/netfoil.nix
Normal file
@@ -0,0 +1,263 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.netfoil;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.netfoil = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Enable Netfoil, a minimal, filtering, DNS proxy";
|
||||
};
|
||||
listen = {
|
||||
port = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 53;
|
||||
description = "Port on which Netfoil listens for incoming connections";
|
||||
};
|
||||
ipAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "IP address on which Netfoil listens for incoming connections";
|
||||
};
|
||||
};
|
||||
logAllowed = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Log allowed DNS queries";
|
||||
};
|
||||
doHUrl = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "https://security.cloudflare-dns.com/dns-query";
|
||||
description = "The DoH URL to use for upstream DNS queries";
|
||||
};
|
||||
doHIPs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "1.1.1.2,1.0.0.2";
|
||||
description = "The DoH IPs to use for upstream DNS queries";
|
||||
};
|
||||
logDenied = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Log denied DNS queries";
|
||||
};
|
||||
config = lib.mkOption {
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
default = { };
|
||||
description = "Additional configuration options for Netfoil";
|
||||
};
|
||||
rules = {
|
||||
allow = {
|
||||
exact = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of exact domain names to allow";
|
||||
};
|
||||
ipv4 = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of ipv4 CIDR ranges to allow";
|
||||
};
|
||||
ipv6 = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of ipv6 CIDR ranges to allow";
|
||||
};
|
||||
tld = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of TLDs to allow";
|
||||
};
|
||||
};
|
||||
deny = {
|
||||
exact = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of exact domain names to deny";
|
||||
};
|
||||
ipv4 = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of ipv4 CIDR ranges to deny";
|
||||
};
|
||||
ipv6 = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of ipv6 CIDR ranges to deny";
|
||||
};
|
||||
tld = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of TLDs to deny";
|
||||
};
|
||||
};
|
||||
known = {
|
||||
knownTlds = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [
|
||||
".com"
|
||||
".net"
|
||||
".org"
|
||||
".edu"
|
||||
".gov"
|
||||
".mil"
|
||||
".int"
|
||||
];
|
||||
description = "List of known TLDs";
|
||||
};
|
||||
};
|
||||
pin = {
|
||||
a = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of A records to pin <domain:ipv4>";
|
||||
};
|
||||
responseDomain = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.str;
|
||||
default = [ ];
|
||||
description = "List of domains to pin <domain:domain>";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (
|
||||
let
|
||||
configFile = lib.concatStringsSep "\n" (
|
||||
[
|
||||
"LogAllowed=${lib.boolToString cfg.logAllowed}"
|
||||
"LogDenied=${lib.boolToString cfg.logDenied}"
|
||||
"DoHURL=${cfg.doHUrl}"
|
||||
"DoHIPs=${cfg.doHIPs}"
|
||||
]
|
||||
++ (map (key: "${key} = \"${cfg.config.${key}}\"") (lib.attrNames cfg.config))
|
||||
++ lib.optional ((lib.length cfg.rules.pin.responseDomain) != 0) "PinResponseDomain=true"
|
||||
);
|
||||
configDir = pkgs.buildEnv {
|
||||
name = "netfoil-config";
|
||||
paths = [
|
||||
(pkgs.writeTextDir "config" configFile)
|
||||
(pkgs.writeTextDir "allow.exact" (lib.concatStringsSep "\n" cfg.rules.allow.exact))
|
||||
(pkgs.writeTextDir "allow.ipv4" (lib.concatStringsSep "\n" cfg.rules.allow.ipv4))
|
||||
(pkgs.writeTextDir "allow.ipv6" (lib.concatStringsSep "\n" cfg.rules.allow.ipv6))
|
||||
(pkgs.writeTextDir "allow.suffix" (lib.concatStringsSep "\n" cfg.rules.allow.tld))
|
||||
(pkgs.writeTextDir "allow.tld" (lib.concatStringsSep "\n" cfg.rules.allow.tld))
|
||||
(pkgs.writeTextDir "deny.exact" (lib.concatStringsSep "\n" cfg.rules.deny.exact))
|
||||
(pkgs.writeTextDir "deny.ipv4" (lib.concatStringsSep "\n" cfg.rules.deny.ipv4))
|
||||
(pkgs.writeTextDir "deny.ipv6" (lib.concatStringsSep "\n" cfg.rules.deny.ipv6))
|
||||
(pkgs.writeTextDir "deny.suffix" (lib.concatStringsSep "\n" cfg.rules.deny.tld))
|
||||
(pkgs.writeTextDir "deny.tld" (lib.concatStringsSep "\n" cfg.rules.deny.tld))
|
||||
(pkgs.writeTextDir "known.tld" (lib.concatStringsSep "\n" cfg.rules.known.knownTlds))
|
||||
(pkgs.writeTextDir "pin.a" (lib.concatStringsSep "\n" cfg.rules.pin.a))
|
||||
(pkgs.writeTextDir "pin.response-domain" (lib.concatStringsSep "\n" cfg.rules.pin.responseDomain))
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
systemd = {
|
||||
services.netfoil = {
|
||||
enable = true;
|
||||
description = "Netfoil DNS proxy";
|
||||
after = [ "network.target" ];
|
||||
requires = [ "netfoil.socket" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${pkgs.netfoil}/bin/netfoil --config-directory ${configDir}";
|
||||
Restart = "always";
|
||||
RestartSec = "5";
|
||||
DynamicUser = true;
|
||||
BindReadOnlyPaths = [
|
||||
"${pkgs.netfoil}"
|
||||
"${configDir}"
|
||||
"/etc/ssl"
|
||||
builtins.storeDir
|
||||
];
|
||||
Slice = "netfoil.slice";
|
||||
AmbientCapabilities = "";
|
||||
CapabilityBoundingSet = [ ];
|
||||
SystenCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@basic-io"
|
||||
"@file-system"
|
||||
"@network-io"
|
||||
"@signal"
|
||||
"@process"
|
||||
"@io-event"
|
||||
"@system-service"
|
||||
"@resources"
|
||||
];
|
||||
RuntimeDirectory = "netfoil";
|
||||
RuntimeDirectoryMode = "0755";
|
||||
RootDirectory = "/run/netfoil";
|
||||
RestrictAddressFamilies = "AF_INET AF_INET6";
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
|
||||
# This might set AllowDevices=char-rtc r
|
||||
ProtectClock = true;
|
||||
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
|
||||
RemoveIPC = true;
|
||||
UMask = "0077";
|
||||
|
||||
# IPC namespace
|
||||
PrivateIPC = true;
|
||||
|
||||
# UTS namespace
|
||||
ProtectHostname = true;
|
||||
|
||||
# Changes mounts (custom is more strict)
|
||||
# https://github.com/systemd/systemd/blob/main/src/core/namespace.c
|
||||
#
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
ProtectSystem = "strict";
|
||||
PrivateTmp = true;
|
||||
|
||||
#
|
||||
# seccomp _sysctl (custom filter does not allow it anyway)
|
||||
# /proc and /sys mounts (custom is more strict)
|
||||
ProtectKernelTunables = true;
|
||||
#
|
||||
# seccomp @raw-io (custom filter does not allow it anyway)
|
||||
PrivateDevices = true;
|
||||
DevicePolicy = "closed";
|
||||
|
||||
SocketBindDeny = "any";
|
||||
|
||||
CPUQuota = "50%";
|
||||
MemoryMax = "100M";
|
||||
TasksMax = "100";
|
||||
};
|
||||
};
|
||||
slices.netfoil = {
|
||||
description = "Slice for Netfoil DNS proxy";
|
||||
};
|
||||
sockets.netfoil = {
|
||||
description = "Netfoil DNS proxy socket";
|
||||
wantedBy = [ "sockets.target" ];
|
||||
socketConfig = {
|
||||
ListenDatagram = "${cfg.listen.ipAddress}:${toString cfg.listen.port}";
|
||||
Service = "netfoil.service";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1064,6 +1064,7 @@ in
|
||||
netbox_4_4 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_4; };
|
||||
netbox_4_5 = handleTest ./web-apps/netbox/default.nix { netbox = pkgs.netbox_4_5; };
|
||||
netdata = runTest ./netdata.nix;
|
||||
netfoil = runTest ./netfoil.nix;
|
||||
networking.networkd = handleTest ./networking/networkd-and-scripted.nix { networkd = true; };
|
||||
networking.networkmanager = handleTest ./networking/networkmanager.nix { };
|
||||
networking.scripted = handleTest ./networking/networkd-and-scripted.nix { networkd = false; };
|
||||
|
||||
28
nixos/tests/netfoil.nix
Normal file
28
nixos/tests/netfoil.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
name = "netfoil";
|
||||
meta.maintainers = with lib.maintainers; [
|
||||
marcusramberg
|
||||
sgo
|
||||
];
|
||||
|
||||
nodes = {
|
||||
one =
|
||||
{ config, ... }:
|
||||
{
|
||||
services.netfoil = {
|
||||
enable = true;
|
||||
listen.port = 6353;
|
||||
};
|
||||
};
|
||||
};
|
||||
interactive.sshBackdoor.enable = true;
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
with subtest("ensure netfoil starts and listens on 6353"):
|
||||
one.wait_for_unit("netfoil.service")
|
||||
one.wait_for_open_port(6353)
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user