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
|
||||||
{
|
{
|
||||||
|
vlan = {
|
||||||
|
lan = {
|
||||||
|
id = 10;
|
||||||
|
subnet = mkSubnet "10.80.1.0/24" "fd00:80:1::/64";
|
||||||
domain = "home.sbruder.de";
|
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 = {
|
tc = {
|
||||||
interface = "enp1s0";
|
interface = "enp1s0";
|
||||||
# 4160 kbit is slightly smaller than the average upload
|
# 4160 kbit is slightly smaller than the average upload
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
# Home network configuration
|
# Home network configuration
|
||||||
# (2.5GbE clients)
|
#
|
||||||
# | |
|
# +----------+ +------------+
|
||||||
# +----------+ +----------+
|
# | | | | (clients)# (guests)
|
||||||
# | | | | | | (1GbE clients)
|
# | | | +--|--|--|--|-#+-|--|--|-|-unused|
|
||||||
# | | | | | +|-|-|-|-|+
|
# +---+----+ +-+-+-+-+-+ | 01 02 …… 12 # 13 …… 24 | 25 26 |
|
||||||
# +---+----+ +-+-+-+-+-+ |5 4 3 2 1|
|
# |upstream| | 1 2 3 4 | | aruba Instant On 1830 | (SFP) |
|
||||||
# |upstream| | 1 2 3 4 | |TL-SG105 |
|
# +--------+ | shinobu | +------------------------+-------+
|
||||||
# +--------+ | shinobu | +---------+
|
|
||||||
# +---------+
|
# +---------+
|
||||||
#
|
#
|
||||||
# It consists of shinobu as a router (this configuration),
|
# 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).
|
# 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:
|
# 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
|
# device → NAT on shinobu → NAT on plastic router → PŸUR CGNAT
|
||||||
#
|
#
|
||||||
# Because the switch only supports GbE,
|
# Because of issues with the NICs operating at 2.5GbE,
|
||||||
# the two clients I currently have with support for 2.5GbE are connected
|
# all clients are connected to the switch,
|
||||||
# directly to the two remaining network interfaces on shinobu.
|
# even if they have a 2.5GbE NIC.
|
||||||
# Once I have more devices with support for 2.5GbE
|
|
||||||
# or I find a good deal on a matching switch,
|
|
||||||
# I will change this.
|
|
||||||
#
|
#
|
||||||
# Wireless is configured by providing the whole hostapd configuration file as a secret.
|
# 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.
|
# Once nixpkgs PR 222536 is merged, I will migrate to using the NixOS module.
|
||||||
|
@ -52,15 +48,48 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
# not all interfaces need to be up
|
# not all interfaces need to be up
|
||||||
wait-online.extraArgs = [ "--any" ];
|
wait-online.extraArgs = [ "--any" ];
|
||||||
netdevs = {
|
netdevs = lib.mkMerge [
|
||||||
br-lan = {
|
(lib.mapAttrs
|
||||||
|
(name: config: {
|
||||||
netdevConfig = {
|
netdevConfig = {
|
||||||
Name = "br-lan";
|
Kind = "vlan";
|
||||||
|
Name = name;
|
||||||
|
};
|
||||||
|
vlanConfig = {
|
||||||
|
Id = config.id;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
cfg.vlan)
|
||||||
|
(lib.mapAttrs'
|
||||||
|
(name: config: lib.nameValuePair "br-${name}" {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = "br-${name}";
|
||||||
Kind = "bridge";
|
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;
|
||||||
};
|
};
|
||||||
networks = {
|
})
|
||||||
|
cfg.vlan)
|
||||||
|
{
|
||||||
wan = {
|
wan = {
|
||||||
name = "enp1s0";
|
name = "enp1s0";
|
||||||
DHCP = "ipv4";
|
DHCP = "ipv4";
|
||||||
|
@ -76,9 +105,17 @@ in
|
||||||
UseDNS = "no";
|
UseDNS = "no";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
lan1 = {
|
physical-lan = {
|
||||||
name = "enp2s0";
|
name = "enp2s0";
|
||||||
bridge = [ "br-lan" ];
|
vlan = lib.attrNames cfg.vlan;
|
||||||
|
# no autoconfiguration needed, only tagged VLAN
|
||||||
|
networkConfig = {
|
||||||
|
LinkLocalAddressing = "no";
|
||||||
|
LLDP = "no";
|
||||||
|
EmitLLDP = "no";
|
||||||
|
IPv6AcceptRA = "no";
|
||||||
|
IPv6SendRA = "no";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
lan2 = {
|
lan2 = {
|
||||||
name = "enp3s0";
|
name = "enp3s0";
|
||||||
|
@ -88,12 +125,8 @@ in
|
||||||
name = "enp4s0";
|
name = "enp4s0";
|
||||||
bridge = [ "br-lan" ];
|
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;
|
services.resolved.enable = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ config, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = pkgs.callPackage ./common.nix { };
|
cfg = pkgs.callPackage ./common.nix { };
|
||||||
in
|
in
|
||||||
|
@ -9,35 +9,39 @@ in
|
||||||
settings = {
|
settings = {
|
||||||
bogus-priv = true; # do not forward revese lookups of internal addresses
|
bogus-priv = true; # do not forward revese lookups of internal addresses
|
||||||
domain-needed = true; # do not forward names without domain
|
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-hosts = true; # do not resolve hosts from /etc/hosts
|
||||||
no-resolv = true; # only use explicitly configured resolvers
|
no-resolv = true; # only use explicitly configured resolvers
|
||||||
|
dhcp-fqdn = true; # only insert qualified names of DHCP clients into DNS
|
||||||
|
|
||||||
cache-size = 10000;
|
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
|
# Allow resolving the router
|
||||||
interface-name = [
|
interface-name = lib.mapAttrsToList (name: { domain, ... }: "${config.networking.hostName}.${domain},br-${name}") cfg.vlan;
|
||||||
"${config.networking.hostName}.${cfg.domain},br-lan"
|
|
||||||
"${config.networking.hostName},br-lan"
|
|
||||||
];
|
|
||||||
|
|
||||||
# DHCPv4
|
dhcp-range = lib.flatten (lib.mapAttrsToList
|
||||||
dhcp-range = [
|
(name: { subnet, ... }: [
|
||||||
"10.80.1.20,10.80.1.150,12h" # DHCPv4
|
"tag:br-${name},${subnet.v4.withoutLastComponent}2,${subnet.v4.withoutLastComponent}254,12h" # DHCPv4
|
||||||
"fd00:80:1::,ra-stateless,ra-names" # SLAAC (for addresses) / DHCPv6 (for DNS)
|
"tag:br-${name},${subnet.v6.net},ra-stateless,ra-names" # SLAAC (for addresses) / DHCPv6 (for DNS)
|
||||||
];
|
])
|
||||||
dhcp-option = [
|
cfg.vlan);
|
||||||
"option:router,10.80.1.1"
|
dhcp-option = lib.flatten (lib.mapAttrsToList
|
||||||
"option6:dns-server,fd00:80:1::1"
|
(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 = [
|
server = [
|
||||||
"127.0.0.1#5053"
|
"127.0.0.1#5053"
|
||||||
];
|
];
|
||||||
|
|
|
@ -10,7 +10,10 @@ let
|
||||||
else lib.generators.mkValueStringDefault { } v;
|
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
|
defines = lib.concatStringsSep
|
||||||
"\n"
|
"\n"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define NAT_LAN_IFACES = { "br-lan" }
|
define NAT_LAN_IFACES = { "br-lan", "br-guest" }
|
||||||
define PHYSICAL_WAN = "enp1s0"
|
define PHYSICAL_WAN = "enp1s0"
|
||||||
define NAT_WAN_IFACES = { $PHYSICAL_WAN }
|
define NAT_WAN_IFACES = { $PHYSICAL_WAN }
|
||||||
|
|
||||||
|
@ -6,9 +6,20 @@ table inet filter {
|
||||||
chain forward {
|
chain forward {
|
||||||
type filter hook forward priority filter; policy drop
|
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_LAN_IFACES oifname $NAT_WAN_IFACES counter accept
|
||||||
iifname $NAT_WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related 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