shinobu/router: Add network segmentation
This commit is contained in:
parent
1740570d00
commit
4611e12772
|
@ -1,6 +1,55 @@
|
|||
{ ... }:
|
||||
{ lib, ... }:
|
||||
let
|
||||
mkSubnet = v4: v6:
|
||||
let
|
||||
splitCidr = lib.splitString "/";
|
||||
fst = lib.flip lib.elemAt 0;
|
||||
snd = lib.flip lib.elemAt 1;
|
||||
|
||||
v4Split = splitCidr v4;
|
||||
v6Split = splitCidr v6;
|
||||
in
|
||||
{
|
||||
v4 = rec {
|
||||
cidr = v4;
|
||||
net = fst v4Split;
|
||||
suffix = snd v4Split;
|
||||
withoutLastComponent = lib.substring 0 ((lib.stringLength net) - 1) net;
|
||||
gateway = "${withoutLastComponent}1";
|
||||
gatewayCidr = "${gateway}/${suffix}";
|
||||
};
|
||||
v6 = rec {
|
||||
cidr = v6;
|
||||
net = fst v6Split;
|
||||
suffix = snd v6Split;
|
||||
gateway = "${net}1";
|
||||
gatewayCidr = "${gateway}/${suffix}";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
domain = "home.sbruder.de";
|
||||
vlan = {
|
||||
lan = {
|
||||
id = 10;
|
||||
subnet = mkSubnet "10.80.1.0/24" "fd00:80:1::/64";
|
||||
domain = "home.sbruder.de";
|
||||
};
|
||||
management = {
|
||||
id = 20;
|
||||
subnet = mkSubnet "10.80.2.0/24" "fd00:80:2::/64";
|
||||
domain = "management.sbruder.de";
|
||||
};
|
||||
guest = {
|
||||
id = 30;
|
||||
subnet = mkSubnet "10.80.3.0/24" "fd00:80:3::/64";
|
||||
domain = "guest.sbruder.de";
|
||||
};
|
||||
iot = {
|
||||
id = 40;
|
||||
subnet = mkSubnet "10.80.4.0/24" "fd00:80:4::/64";
|
||||
domain = "iot.sbruder.de";
|
||||
};
|
||||
};
|
||||
tc = {
|
||||
interface = "enp1s0";
|
||||
# 4160 kbit is slightly smaller than the average upload
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
# Home network configuration
|
||||
# (2.5GbE clients)
|
||||
# | |
|
||||
# +----------+ +----------+
|
||||
# | | | | | | (1GbE clients)
|
||||
# | | | | | +|-|-|-|-|+
|
||||
# +---+----+ +-+-+-+-+-+ |5 4 3 2 1|
|
||||
# |upstream| | 1 2 3 4 | |TL-SG105 |
|
||||
# +--------+ | shinobu | +---------+
|
||||
#
|
||||
# +----------+ +------------+
|
||||
# | | | | (clients)# (guests)
|
||||
# | | | +--|--|--|--|-#+-|--|--|-|-unused|
|
||||
# +---+----+ +-+-+-+-+-+ | 01 02 …… 12 # 13 …… 24 | 25 26 |
|
||||
# |upstream| | 1 2 3 4 | | aruba Instant On 1830 | (SFP) |
|
||||
# +--------+ | shinobu | +------------------------+-------+
|
||||
# +---------+
|
||||
#
|
||||
# It consists of shinobu as a router (this configuration),
|
||||
# connected to a TP-LINK TL-SG105E “smart managed” (i.e., it can do VLANs) 5-port switch.
|
||||
# connected to a aruba (HPE) Instant ON 1830 24-port 1GbE switch.
|
||||
# The upstream comes (for now) from a PŸUR “WLAN-Kabelbox” (Compal CH7467CE).
|
||||
# Sadly, I could not enable bridge mode on it, so the packets now go through (at least) three layers of NAT:
|
||||
# device → NAT on shinobu → NAT on plastic router → PŸUR CGNAT
|
||||
#
|
||||
# Because the switch only supports GbE,
|
||||
# the two clients I currently have with support for 2.5GbE are connected
|
||||
# directly to the two remaining network interfaces on shinobu.
|
||||
# Once I have more devices with support for 2.5GbE
|
||||
# or I find a good deal on a matching switch,
|
||||
# I will change this.
|
||||
# Because of issues with the NICs operating at 2.5GbE,
|
||||
# all clients are connected to the switch,
|
||||
# even if they have a 2.5GbE NIC.
|
||||
#
|
||||
# Wireless is configured by providing the whole hostapd configuration file as a secret.
|
||||
# Once nixpkgs PR 222536 is merged, I will migrate to using the NixOS module.
|
||||
|
@ -52,48 +48,85 @@ in
|
|||
enable = true;
|
||||
# not all interfaces need to be up
|
||||
wait-online.extraArgs = [ "--any" ];
|
||||
netdevs = {
|
||||
br-lan = {
|
||||
netdevConfig = {
|
||||
Name = "br-lan";
|
||||
Kind = "bridge";
|
||||
netdevs = lib.mkMerge [
|
||||
(lib.mapAttrs
|
||||
(name: config: {
|
||||
netdevConfig = {
|
||||
Kind = "vlan";
|
||||
Name = name;
|
||||
};
|
||||
vlanConfig = {
|
||||
Id = config.id;
|
||||
};
|
||||
})
|
||||
cfg.vlan)
|
||||
(lib.mapAttrs'
|
||||
(name: config: lib.nameValuePair "br-${name}" {
|
||||
netdevConfig = {
|
||||
Name = "br-${name}";
|
||||
Kind = "bridge";
|
||||
};
|
||||
})
|
||||
cfg.vlan)
|
||||
];
|
||||
networks = lib.mkMerge [
|
||||
(lib.mapAttrs
|
||||
(name: config: {
|
||||
inherit name;
|
||||
matchConfig = {
|
||||
Type = "vlan";
|
||||
};
|
||||
bridge = [ "br-${name}" ];
|
||||
})
|
||||
cfg.vlan)
|
||||
(lib.mapAttrs'
|
||||
(name: config: lib.nameValuePair "br-${name}" {
|
||||
name = "br-${name}";
|
||||
domains = [ config.domain ];
|
||||
address = lib.mapAttrsToList (family: familyConfig: familyConfig.gatewayCidr) config.subnet;
|
||||
networkConfig = {
|
||||
IPv6AcceptRA = false;
|
||||
};
|
||||
})
|
||||
cfg.vlan)
|
||||
{
|
||||
wan = {
|
||||
name = "enp1s0";
|
||||
DHCP = "ipv4";
|
||||
networkConfig = {
|
||||
IPv6AcceptRA = "yes";
|
||||
};
|
||||
dhcpV4Config = {
|
||||
UseDNS = "no";
|
||||
};
|
||||
ipv6AcceptRAConfig = {
|
||||
# Only use RA
|
||||
DHCPv6Client = false;
|
||||
UseDNS = "no";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
networks = {
|
||||
wan = {
|
||||
name = "enp1s0";
|
||||
DHCP = "ipv4";
|
||||
networkConfig = {
|
||||
IPv6AcceptRA = "yes";
|
||||
physical-lan = {
|
||||
name = "enp2s0";
|
||||
vlan = lib.attrNames cfg.vlan;
|
||||
# no autoconfiguration needed, only tagged VLAN
|
||||
networkConfig = {
|
||||
LinkLocalAddressing = "no";
|
||||
LLDP = "no";
|
||||
EmitLLDP = "no";
|
||||
IPv6AcceptRA = "no";
|
||||
IPv6SendRA = "no";
|
||||
};
|
||||
};
|
||||
dhcpV4Config = {
|
||||
UseDNS = "no";
|
||||
lan2 = {
|
||||
name = "enp3s0";
|
||||
bridge = [ "br-lan" ];
|
||||
};
|
||||
ipv6AcceptRAConfig = {
|
||||
# Only use RA
|
||||
DHCPv6Client = false;
|
||||
UseDNS = "no";
|
||||
lan3 = {
|
||||
name = "enp4s0";
|
||||
bridge = [ "br-lan" ];
|
||||
};
|
||||
};
|
||||
lan1 = {
|
||||
name = "enp2s0";
|
||||
bridge = [ "br-lan" ];
|
||||
};
|
||||
lan2 = {
|
||||
name = "enp3s0";
|
||||
bridge = [ "br-lan" ];
|
||||
};
|
||||
lan3 = {
|
||||
name = "enp4s0";
|
||||
bridge = [ "br-lan" ];
|
||||
};
|
||||
br-lan = {
|
||||
name = "br-lan";
|
||||
domains = [ cfg.domain ];
|
||||
address = [ "10.80.1.1/24" "fd00:80:1::1/64" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
services.resolved.enable = false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{ config, pkgs, ... }:
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = pkgs.callPackage ./common.nix { };
|
||||
in
|
||||
|
@ -9,35 +9,39 @@ in
|
|||
settings = {
|
||||
bogus-priv = true; # do not forward revese lookups of internal addresses
|
||||
domain-needed = true; # do not forward names without domain
|
||||
interface = "br-lan"; # only respond to queries from lan
|
||||
interface = lib.mapAttrsToList (name: config: "br-${name}") cfg.vlan; # only respond to queries from own interfaces
|
||||
no-hosts = true; # do not resolve hosts from /etc/hosts
|
||||
no-resolv = true; # only use explicitly configured resolvers
|
||||
dhcp-fqdn = true; # only insert qualified names of DHCP clients into DNS
|
||||
|
||||
cache-size = 10000;
|
||||
|
||||
inherit (cfg) domain;
|
||||
domain = [
|
||||
"invalid.sbruder.de" # used when no rule below matches
|
||||
] ++ (lib.flatten (lib.mapAttrsToList
|
||||
(name: { domain, subnet, ... }: [
|
||||
"${domain},br-${name}" # only this is not enough
|
||||
"${domain},${subnet.v4.cidr}"
|
||||
"${domain},${subnet.v6.cidr}"
|
||||
])
|
||||
cfg.vlan));
|
||||
|
||||
# Allow resolving the router
|
||||
interface-name = [
|
||||
"${config.networking.hostName}.${cfg.domain},br-lan"
|
||||
"${config.networking.hostName},br-lan"
|
||||
];
|
||||
interface-name = lib.mapAttrsToList (name: { domain, ... }: "${config.networking.hostName}.${domain},br-${name}") cfg.vlan;
|
||||
|
||||
# DHCPv4
|
||||
dhcp-range = [
|
||||
"10.80.1.20,10.80.1.150,12h" # DHCPv4
|
||||
"fd00:80:1::,ra-stateless,ra-names" # SLAAC (for addresses) / DHCPv6 (for DNS)
|
||||
];
|
||||
dhcp-option = [
|
||||
"option:router,10.80.1.1"
|
||||
"option6:dns-server,fd00:80:1::1"
|
||||
];
|
||||
dhcp-range = lib.flatten (lib.mapAttrsToList
|
||||
(name: { subnet, ... }: [
|
||||
"tag:br-${name},${subnet.v4.withoutLastComponent}2,${subnet.v4.withoutLastComponent}254,12h" # DHCPv4
|
||||
"tag:br-${name},${subnet.v6.net},ra-stateless,ra-names" # SLAAC (for addresses) / DHCPv6 (for DNS)
|
||||
])
|
||||
cfg.vlan);
|
||||
dhcp-option = lib.flatten (lib.mapAttrsToList
|
||||
(name: { subnet, ... }: [
|
||||
"tag:br-${name},option:router,${subnet.v4.gateway}"
|
||||
"tag:br-${name},option6:dns-server,${subnet.v6.gateway}"
|
||||
])
|
||||
cfg.vlan);
|
||||
|
||||
# Despite its name, the switch does not have a “smart” configuration,
|
||||
# that would allow me to tell it not to get DHCP from wan,
|
||||
# but from lan instead.
|
||||
# So it has to use static configuration.
|
||||
host-record = "switchviech,switchviech.${cfg.domain},10.80.1.19";
|
||||
server = [
|
||||
"127.0.0.1#5053"
|
||||
];
|
||||
|
|
|
@ -10,7 +10,10 @@ let
|
|||
else lib.generators.mkValueStringDefault { } v;
|
||||
} " = ";
|
||||
|
||||
passthru = { };
|
||||
passthru = {
|
||||
VLANS = lib.attrNames cfg.vlan;
|
||||
VLAN_BRIDGES = map (name: "br-${name}") (lib.attrNames cfg.vlan);
|
||||
};
|
||||
|
||||
defines = lib.concatStringsSep
|
||||
"\n"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
define NAT_LAN_IFACES = { "br-lan" }
|
||||
define NAT_LAN_IFACES = { "br-lan", "br-guest" }
|
||||
define PHYSICAL_WAN = "enp1s0"
|
||||
define NAT_WAN_IFACES = { $PHYSICAL_WAN }
|
||||
|
||||
|
@ -6,9 +6,20 @@ table inet filter {
|
|||
chain forward {
|
||||
type filter hook forward priority filter; policy drop
|
||||
|
||||
# allow traffic between lan and wan
|
||||
# plastic router, might be vulnerable (FIXME v6 is still reachable)
|
||||
iifname "br-guest" ip daddr "192.168.0.1" drop
|
||||
|
||||
# allow traffic between selected VLANs and wan
|
||||
iifname $NAT_LAN_IFACES oifname $NAT_WAN_IFACES counter accept
|
||||
iifname $NAT_WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related counter accept
|
||||
|
||||
# traffic from lan to all other vlans is allowed
|
||||
iifname "br-lan" oifname $VLAN_BRIDGES counter accept;
|
||||
iifname $VLAN_BRIDGES oifname "br-lan" ct state established,related counter accept
|
||||
|
||||
iifname "br-iot" ip daddr 167.235.30.249 tcp dport 1883 counter accept # FIXME migrate service to shinobu
|
||||
iifname "br-iot" udp dport 123 counter accept # FIXME too generic
|
||||
iifname $NAT_WAN_IFACES oifname "br-iot" ct state established,related counter accept
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue