diff options
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/default.nix | 9 | ||||
| -rw-r--r-- | sys/net/fail2ban.nix | 37 | ||||
| -rw-r--r-- | sys/net/interfaces.nix | 120 | ||||
| -rw-r--r-- | sys/net/nets.nix | 1 | ||||
| -rw-r--r-- | sys/net/options.nix | 278 | ||||
| -rw-r--r-- | sys/net/vsock.nix | 63 |
6 files changed, 508 insertions, 0 deletions
diff --git a/sys/net/default.nix b/sys/net/default.nix new file mode 100644 index 0000000..c3c5740 --- /dev/null +++ b/sys/net/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./fail2ban.nix + ./interfaces.nix + ./nets.nix + ./options.nix + ./vsock.nix + ]; +} diff --git a/sys/net/fail2ban.nix b/sys/net/fail2ban.nix new file mode 100644 index 0000000..32197b6 --- /dev/null +++ b/sys/net/fail2ban.nix @@ -0,0 +1,37 @@ +{ + lib, + config, + pkgs, + ... +}: +with lib; let + cfg = config.local.net.fail2ban; + inherit (config.local) nets; +in { + options.local.net.fail2ban = { + enable = mkEnableOption "fail2ban"; + }; + + config = mkIf cfg.enable { + services.fail2ban = { + enable = true; + + bantime = "10m"; + + bantime-increment = { + enable = true; + + maxtime = "48h"; + rndtime = "10m"; + overalljails = true; + }; + + ignoreIP = [ + nets.static-vpn.v6.cidr + nets.gate-srv.v6.cidr + nets.gate-public.hosts.gate.v4.address + nets.gate-public.hosts.gate.v6.address + ]; + }; + }; +} diff --git a/sys/net/interfaces.nix b/sys/net/interfaces.nix new file mode 100644 index 0000000..3150b02 --- /dev/null +++ b/sys/net/interfaces.nix @@ -0,0 +1,120 @@ +{ + lib, + config, + pkgs, + ... +}: +with lib; let + cfg = config.local.net; +in { + options.local.net = with lib.types; { + enable = mkEnableOption "networking stack"; + + hostname = mkOption { + type = str; + }; + + dhcpInterface = mkOption { + type = nullOr str; + default = null; + }; + }; + + config = mkIf cfg.enable { + boot.kernel.sysctl = { + # rp_filter=1 reemplazado por nixos-fw-rpfilter + "net.ipv4.conf.all.rp_filter" = mkForce 2; + "net.ipv4.conf.default.rp_filter" = mkForce 2; + + "net.ipv4.conf.all.forwarding" = mkForce true; + "net.ipv6.conf.all.forwarding" = mkForce true; + "net.ipv4.conf.default.forwarding" = mkForce true; + "net.ipv6.conf.default.forwarding" = mkForce true; + + "net.ipv4.conf.all.accept_redirects" = mkForce false; + "net.ipv6.conf.all.accept_redirects" = mkForce false; + "net.ipv4.conf.default.accept_redirects" = mkForce false; + "net.ipv6.conf.default.accept_redirects" = mkForce false; + }; + + environment.systemPackages = with pkgs; [ + conntrack-tools + dhcpcd + dnsutils + nmap + socat + tcpdump + wireguard-tools + ]; + + networking = { + domain = mkDefault config.local.domains.host.main; + hostName = cfg.hostname; + + firewall = { + logReversePathDrops = true; + checkReversePath = "loose"; + + extraCommands = mkBefore '' + ip46tables -t filter -P INPUT DROP + ip46tables -t filter -P FORWARD ACCEPT #TODO: DROP + + ip46tables -t filter -N local-input + ip46tables -t filter -N local-forward + ip46tables -t nat -N local-prerouting + ip46tables -t nat -N local-postrouting + + ip46tables -t filter -I INPUT -j local-input + ip46tables -t filter -I FORWARD -j local-forward + ip46tables -t nat -I PREROUTING -j local-prerouting + ip46tables -t nat -I POSTROUTING -j local-postrouting + + ip46tables -t filter -A local-forward -m conntrack --ctstate RELATED,ESTABLISHED,SNAT,DNAT -j ACCEPT + ''; + + extraStopCommands = mkAfter '' + ip46tables -t filter -D INPUT -j local-input || true + ip46tables -t filter -D FORWARD -j local-forward || true + ip46tables -t nat -D PREROUTING -j local-prerouting || true + ip46tables -t nat -D POSTROUTING -j local-postrouting || true + + ip46tables -t filter -F local-input || true + ip46tables -t filter -X local-input || true + ip46tables -t filter -F local-forward || true + ip46tables -t filter -X local-forward || true + ip46tables -t nat -F local-prerouting || true + ip46tables -t nat -X local-prerouting || true + ip46tables -t nat -F local-postrouting || true + ip46tables -t nat -X local-postrouting || true + + ip46tables -t filter -P INPUT ACCEPT + ip46tables -t filter -P FORWARD ACCEPT + ''; + + logRefusedConnections = false; + }; + + useDHCP = false; + enableIPv6 = mkDefault true; + useNetworkd = mkDefault true; + useHostResolvConf = false; + + wireguard.enable = true; + }; + + systemd.network.networks = mkIf (cfg.dhcpInterface != null) { + "40-${cfg.dhcpInterface}" = { + matchConfig.Name = cfg.dhcpInterface; + + networkConfig = { + DHCP = "ipv4"; + IPv6AcceptRA = true; + IPv6PrivacyExtensions = "kernel"; + }; + + # make routing on this interface a dependency for network-online.target + linkConfig.RequiredForOnline = "routable"; + }; + }; + }; +} diff --git a/sys/net/nets.nix b/sys/net/nets.nix new file mode 100644 index 0000000..1bb3788 --- /dev/null +++ b/sys/net/nets.nix @@ -0,0 +1 @@ +# This file has been lustrated. diff --git a/sys/net/options.nix b/sys/net/options.nix new file mode 100644 index 0000000..0608fb9 --- /dev/null +++ b/sys/net/options.nix @@ -0,0 +1,278 @@ +{ + config, + lib, + ... +}: +with lib; let + v4PtrHierarchy = address: bits: reverseList (sublist 0 (bits / 8) (splitString "." address)); + + v6PtrHierarchy = address: bits: let + separator = lists.findFirstIndex (hextet: hextet == "") null colonSplit; + colonSplit = splitString ":" address; + + zeroFill = replicate (8 - length colonSplit + 1) "0000"; + leftSplit = sublist 0 separator colonSplit; + rightSplit = sublist (separator + 1) (length colonSplit - separator - 1) colonSplit; + + fullSplit = + if separator != null + then leftSplit ++ zeroFill ++ rightSplit + else colonSplit; + + padded = map (hextet: strings.replicate (4 - stringLength hextet) "0" + hextet) fullSplit; + in + reverseList (sublist 0 (bits / 4) (flatten (map stringToCharacters padded))); + + matchPtrRecordName = { + splitter, + netAddress, + netBits, + targetAddress, + targetBits, + }: let + netSplit = splitter netAddress netBits; + targetSplit = splitter targetAddress targetBits; + + netLength = length netSplit; + lengthDelta = length targetSplit - netLength; + + withinNet = lengthDelta >= 0 && sublist lengthDelta netLength targetSplit == netSplit; + throwMessage = "${targetAddress}/${toString targetBits} is not a subset of ${netAddress}/${toString netBits}"; + + recordHierarchy = sublist 0 lengthDelta targetSplit; + + recordName = + if recordHierarchy != [] + then concatStringsSep "." recordHierarchy + else "@"; + in + throwIfNot withinNet throwMessage recordName; +in { + options.local.nets = with lib.types; + mkOption { + readOnly = true; + + type = attrsOf (submodule ({config, ...}: { + options = let + v4config = config.v4; + v6config = config.v6; + in { + hosts = mkOption { + default = {}; + + type = attrsOf (submodule { + options = { + v4 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + suffix = mkOption { + type = str; + }; + + address = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + single = mkOption { + type = str; + readOnly = true; + }; + }; + + config = { + address = + if v4config.bits == 0 + then config.suffix + else if v4config.bits == 32 + then v4config.subnet + else "${v4config.prefix}.${config.suffix}"; + + cidr = "${config.address}/${toString v4config.bits}"; + single = "${config.address}/32"; + }; + })); + }; + + v6 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + suffix = mkOption { + type = str; + }; + + address = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + single = mkOption { + type = str; + readOnly = true; + }; + }; + + config = { + address = let + hextets = fragment: length (splitString ":" fragment); + separator = + if doubleColon + then "::" + else ":"; + doubleColon = hextets v6config.prefix + hextets config.suffix < 8; + + joined = + if v6config.bits == 128 + then v6config.prefix + else if v6config.bits == 0 + then config.suffix + else "${v6config.prefix}${separator}${config.suffix}"; + in + joined; + + cidr = "${config.address}/${toString v6config.bits}"; + single = "${config.address}/128"; + }; + })); + }; + }; + }); + }; + + v4 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + bits = mkOption { + type = enum [0 8 16 24 32]; + }; + + prefix = mkOption { + type = str; + }; + + subnet = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + ptrDomain = mkOption { + type = str; + readOnly = true; + }; + + ptrRecordName = mkOption { + type = functionTo (functionTo str); + readOnly = true; + }; + }; + + config = { + cidr = "${config.subnet}/${toString config.bits}"; + + subnet = + if config.bits != 0 + then config.prefix + strings.replicate (4 - config.bits / 8) ".0" + else "0.0.0.0"; + + ptrDomain = concatStrings (map (x: x + ".") (v4PtrHierarchy config.subnet config.bits)) + "in-addr.arpa"; + + ptrRecordName = address: bits: + matchPtrRecordName { + splitter = v4PtrHierarchy; + + netBits = config.bits; + netAddress = config.subnet; + + targetBits = bits; + targetAddress = address; + }; + }; + })); + }; + + v6 = mkOption { + default = null; + + type = nullOr (submodule ({config, ...}: { + options = { + bits = mkOption { + type = + addCheck (ints.between 0 128) (b: mod b 4 == 0) + // { + description = "IPv6 subnet bits at nibble boundary"; + }; + }; + + prefix = mkOption { + type = str; + }; + + subnet = mkOption { + type = str; + readOnly = true; + }; + + cidr = mkOption { + type = str; + readOnly = true; + }; + + ptrDomain = mkOption { + type = str; + readOnly = true; + }; + + ptrRecordName = mkOption { + type = functionTo (functionTo str); + readOnly = true; + }; + }; + + config = { + cidr = "${config.subnet}/${toString config.bits}"; + + subnet = + if config.bits == 128 || length (splitString "::" config.prefix) > 1 + then config.prefix + else "${config.prefix}::"; + + ptrDomain = concatStrings (map (x: x + ".") (v6PtrHierarchy config.subnet config.bits)) + "ip6.arpa"; + + ptrRecordName = address: bits: + matchPtrRecordName { + splitter = v6PtrHierarchy; + + netBits = config.bits; + netAddress = config.subnet; + + targetBits = bits; + targetAddress = address; + }; + }; + })); + }; + }; + })); + }; +} diff --git a/sys/net/vsock.nix b/sys/net/vsock.nix new file mode 100644 index 0000000..c6b0ad6 --- /dev/null +++ b/sys/net/vsock.nix @@ -0,0 +1,63 @@ +{ + lib, + config, + pkgs, + ... +}: +with lib; let + cfg = config.local.net.vsock; +in { + options.local.net.vsock = { + connect = mkOption { + default = {}; + type = with lib.types; + attrsOf (submodule ({name, ...}: { + options = { + enable = mkEnableOption "vsock connect '${name}'"; + + cid = mkOption { + type = ints.u32; + default = 2; + }; + + localPort = mkOption { + type = port; + }; + + vsockPort = mkOption { + type = port; + }; + }; + })); + }; + }; + + config = { + systemd = let + connects = + mapAttrs + (_: connect: { + service.serviceConfig = { + Type = "simple"; + ExecStart = "${getExe pkgs.socat} - VSOCK:${toString connect.cid}:${toString connect.vsockPort}"; + StandardInput = "socket"; + }; + + socket = { + wantedBy = ["sockets.target"]; + + socketConfig = { + Accept = true; + ListenStream = "[::1]:${toString connect.localPort}"; + }; + + unitConfig.ConditionVirtualization = "kvm"; + }; + }) + cfg.connect; + in { + sockets = mapAttrs' (name: connect: nameValuePair "vsock-${name}" connect.socket) connects; + services = mapAttrs' (name: connect: nameValuePair "vsock-${name}@" connect.service) connects; + }; + }; +} |
