nixos-module/container/bird: implement upstream failover

This commit is contained in:
Astro 2021-04-29 01:44:48 +02:00
parent ce49c22d2e
commit 257e6686b9
4 changed files with 193 additions and 92 deletions

View File

@ -58,7 +58,8 @@ in
config.site.hosts = lib.mkMerge (
[
{
{ # Static definitions
mgmt-gw.firewall.enable = true;
priv13-gw.firewall.enable = true;
@ -74,8 +75,24 @@ in
services.dnscache.enable = true;
};
c3d2-gw.ospf.allowedUpstreams = [ "upstream1" "upstream2" ];
serv-gw.ospf.allowedUpstreams = [ "upstream1" "upstream2" ];
cls-gw.ospf.allowedUpstreams = [ "upstream1" "upstream2" ];
mgmt-gw.ospf.allowedUpstreams = [ "upstream1" "upstream2" ];
bgp.ospf.allowedUpstreams = [ "upstream1" "upstream2" ];
}
(
builtins.mapAttrs (hostName: _: {
ospf.allowedUpstreams = [ "upstream2" "upstream1" ];
}) (
lib.filterAttrs (hostName: _:
builtins.match "priv[[:digit:]]+-gw" hostName != null
) pillar.containers
)
)
(builtins.foldl' (result: hostName: result // {
"${hostName}" = {
role = "server";

View File

@ -202,6 +202,11 @@ let
default = [];
description = "Additional IPv6 networks to announce";
};
ospf.allowedUpstreams = mkOption {
type = with types; listOf str;
default = [];
description = "Accept default routes from these OSPF routers, in order of preference";
};
wireguard = mkOption {
default = {};
type = with types; attrsOf (submodule (
@ -291,8 +296,23 @@ in
)).dup;
reportCollisions = name: getter: xs:
map (k: "Duplicate ${name}: ${k}") (findCollisions getter xs);
ospfUpstreamXorGw =
builtins.concatMap (hostName:
let
hostConf = config.site.hosts.${hostName};
gwNets = builtins.filter (netName:
hostConf.interfaces.${netName}.gw4 != null
) (builtins.attrNames hostConf.interfaces);
in if gwNets != [] && hostConf.ospf.allowedUpstreams != []
then [ ''
Host ${hostName} has gateway on ${builtins.head gwNets} but accepts default routes from OSPF
'' ]
else []
) (builtins.attrNames config.site.hosts);
in
(reportCollisions "VLAN tag" (x: [x.vlan]) config.site.net) ++
(reportCollisions "IPv4 subnet" (x: if x.subnet4 == null then [] else [x.subnet4]) config.site.net) ++
(reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net);
(reportCollisions "IPv6 subnet" (x: builtins.attrValues x.subnets6) config.site.net) ++
ospfUpstreamXorGw;
}

View File

@ -4,6 +4,10 @@
let
hostConf = config.site.hosts.${hostName};
isUpstream = builtins.any (net:
hostConf.interfaces.${net}.upstream != null
) (builtins.attrNames hostConf.interfaces);
# Configuring a gateway? If so, this is the associated net.
gatewayNet =
let
@ -29,13 +33,33 @@ in
router id ${config.site.net.core.hosts4.${hostName}};
protocol kernel K4 {
learn;
ipv4 {
export all;
${lib.optionalString isUpstream ''
# Learn the default route
import filter {
if net ~ [ 0.0.0.0/0 ] then {
accept;
}
reject;
};
''}
};
}
protocol kernel K6 {
learn;
ipv6 {
export all;
${lib.optionalString isUpstream ''
# Learn the default route
import filter {
if net ~ [ ::/0 ] then {
accept;
}
reject;
};
''}
};
}
protocol device {
@ -67,6 +91,26 @@ in
# OSPFv2 for site-local IPv4
protocol ospf v2 ZW4 {
ipv4 {
export all;
import filter {
if net ~ [ 0.0.0.0/0 ] then {
${(builtins.foldl' (result: gateway: {
text = ''
${result.text}
if ospf_router_id = ${config.site.net.core.hosts4.${gateway}} then {
preference = preference + ${toString result.n};
accept;
}
'';
n = result.n - 10;
}) { text = ""; n = 900; } config.site.hosts.${hostName}.ospf.allowedUpstreams
).text}
reject;
}
accept;
};
};
area 0 {
# Enabled on these networks
networks {
@ -107,6 +151,26 @@ in
# OSPFv3 for site-local IPv6
protocol ospf v3 ZW6 {
ipv6 {
export all;
import filter {
if net ~ [ ::/0 ] then {
${(builtins.foldl' (result: gateway: {
text = ''
${result.text}
if ospf_router_id = ${config.site.net.core.hosts4.${gateway}} then {
preference = preference + ${toString result.n};
accept;
}
'';
n = result.n - 10;
}) { text = ""; n = 900; } config.site.hosts.${hostName}.ospf.allowedUpstreams
).text}
reject;
}
accept;
};
};
area 0 {
# Enabled on these networks
networks {

View File

@ -14,8 +14,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream1
gw6: upstream1
# gw: upstream1
# gw6: upstream1
hwaddr: 0A:14:48:01:06:01
serv:
type: veth
@ -25,8 +25,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream1
gw6: upstream1
# gw: upstream1
# gw6: upstream1
hwaddr: 0A:14:48:01:06:03
cluster:
type: phys
@ -36,8 +36,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:19:00
priv1:
type: phys
@ -47,8 +47,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:18:00
priv2:
type: phys
@ -58,8 +58,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:08:00
priv3:
type: phys
@ -69,8 +69,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:17:01
priv4:
type: phys
@ -80,8 +80,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:12:00
priv5:
type: phys
@ -91,8 +91,8 @@ containers:
interfaces:
core:
type: veth
gw: anon1
gw6: upstream2
# gw: anon1
# gw6: upstream2
hwaddr: 0A:14:48:01:11:00
priv6:
type: phys
@ -102,8 +102,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:10:00
priv7:
type: phys
@ -113,8 +113,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:09:00
priv8:
type: phys
@ -124,8 +124,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:20:00
priv9:
type: phys
@ -135,8 +135,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:13:02
priv10:
type: phys
@ -146,8 +146,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:29:00
priv11:
type: phys
@ -157,8 +157,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:00
priv12:
type: phys
@ -168,8 +168,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:10
priv13:
type: phys
@ -179,8 +179,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:12
priv14:
type: phys
@ -190,8 +190,8 @@ containers:
interfaces:
core:
type: veth
gw: anon1
gw6: upstream2
# gw: anon1
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:14
priv15:
type: phys
@ -201,8 +201,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:16
priv16:
type: phys
@ -212,8 +212,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:18
priv17:
type: phys
@ -223,8 +223,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:1A
priv18:
type: phys
@ -234,8 +234,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:1C
priv19:
type: phys
@ -245,8 +245,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:1E
priv20:
type: phys
@ -256,8 +256,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:20
priv21:
type: phys
@ -267,8 +267,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:24
priv22:
type: phys
@ -278,8 +278,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:22
priv23:
type: phys
@ -289,8 +289,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:26
priv24:
type: phys
@ -300,8 +300,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:28
priv25:
type: phys
@ -311,8 +311,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:2A
priv26:
type: phys
@ -322,8 +322,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:2C
priv27:
type: phys
@ -333,8 +333,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:2E
priv28:
type: phys
@ -344,8 +344,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:30
priv29:
type: phys
@ -355,8 +355,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:32
priv30:
type: phys
@ -366,8 +366,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:34
priv31:
type: phys
@ -377,8 +377,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:36
priv32:
type: phys
@ -388,8 +388,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:38
priv33:
type: phys
@ -399,8 +399,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:40
priv34:
type: phys
@ -410,8 +410,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:42
priv35:
type: phys
@ -421,8 +421,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:44
priv36:
type: phys
@ -432,8 +432,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:46
priv37:
type: phys
@ -443,8 +443,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:48
priv38:
type: phys
@ -454,8 +454,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:4A
priv39:
type: phys
@ -465,8 +465,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream2
gw6: upstream2
# gw: upstream2
# gw6: upstream2
hwaddr: 0A:14:48:01:2A:4C
priv40:
type: phys
@ -504,8 +504,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream1
gw6: upstream1
# gw: upstream1
# gw6: upstream1
hwaddr: 0A:14:48:01:21:00
c3d2:
type: veth
@ -526,8 +526,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream1
gw6: upstream1
# gw: upstream1
# gw6: upstream1
hwaddr: 0A:14:48:01:22:00
c3d2:
type: veth
@ -561,8 +561,8 @@ containers:
interfaces:
core:
type: veth
gw: upstream1
gw6: upstream1
# gw: upstream1
# gw6: upstream1
hwaddr: 0A:14:48:01:24:01
mgmt:
type: veth