diff --git a/go.mod b/go.mod index de23c25..16eefe0 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/coreos/go-systemd/v22 v22.1.0 github.com/google/go-cmp v0.5.4 github.com/magiconair/properties v1.8.4 // indirect - github.com/metal-stack/metal-go v0.11.1 - github.com/metal-stack/metal-lib v0.6.6 + github.com/metal-stack/metal-go v0.13.0 + github.com/metal-stack/metal-lib v0.6.9 github.com/metal-stack/v v1.0.2 github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/pelletier/go-toml v1.8.1 // indirect @@ -17,10 +17,11 @@ require ( github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.16.0 - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + inet.af/netaddr v0.0.0-20210129185718-d0669448cef6 ) diff --git a/go.sum b/go.sum index 0c3d08d..9f875dc 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,7 @@ github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/emicklei/go-restful-openapi/v2 v2.2.1/go.mod h1:bs67E3SEVgSmB3qDuRLqpS0NcpheqtsCCMhW2/jml1E= github.com/emicklei/go-restful/v3 v3.0.0-rc2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.3.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -411,13 +412,13 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/metal-stack/masterdata-api v0.8.3 h1:Hb4TDDp1HwJUalG1SKp14lctWyUWi2Xx4Iq7bPM0R48= github.com/metal-stack/masterdata-api v0.8.3/go.mod h1:vNzStBft4l8ItkUNu7mrdHbSF6kcPLhulByXBifYLkA= -github.com/metal-stack/metal-go v0.11.1 h1:aBv/JolspX3YYQDgLrfDAolJpm0HsQ5yf7H4Kv/o/hE= -github.com/metal-stack/metal-go v0.11.1/go.mod h1:A1ZZSxY8gLIH+cHboUytRxzt42hgnNSCHzfzXwCrRe8= +github.com/metal-stack/metal-go v0.13.0 h1:aXhva8ayTeS2Y4mpcjsM79Cj2Wc30nk20LEkX/guZJk= +github.com/metal-stack/metal-go v0.13.0/go.mod h1:A1ZZSxY8gLIH+cHboUytRxzt42hgnNSCHzfzXwCrRe8= github.com/metal-stack/metal-lib v0.6.0/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= github.com/metal-stack/metal-lib v0.6.4 h1:7lvQcjGrZa3gUIzlbFi61fqFatDzlmWWMvXoqOi5HHM= github.com/metal-stack/metal-lib v0.6.4/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= -github.com/metal-stack/metal-lib v0.6.6 h1:5ajMDUGHruYOmRqn3r373rbIKbTtQkfVfKLKmRatJnE= -github.com/metal-stack/metal-lib v0.6.6/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= +github.com/metal-stack/metal-lib v0.6.9 h1:6AvJ8RKJqjed2GdKIZlN9Qvt8sXhu3r6yfYKv++pLaU= +github.com/metal-stack/metal-lib v0.6.9/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= github.com/metal-stack/security v0.4.0 h1:NrPm5srgmgeS9UdQmGKLEJ3P7BSsV2Gm7P781LmM0Xo= github.com/metal-stack/security v0.4.0/go.mod h1:C7kSrHwRcG+47375RJjhakN1LenbEJF9uQd4I50nZlY= github.com/metal-stack/v v1.0.2 h1:IGtLAGtazQd8r0i/5+YNjBJUEIZYrbVxynY9EXrlTV4= @@ -547,6 +548,7 @@ github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -555,6 +557,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/testcontainers/testcontainers-go v0.7.0/go.mod h1:4dloDPrC94+8ebXA+Iei3Jy+gxF6uHQssJkB3mlP9Rg= @@ -604,6 +608,11 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= +go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= @@ -757,8 +766,8 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -943,6 +952,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -956,6 +967,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k= honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +inet.af/netaddr v0.0.0-20210129185718-d0669448cef6 h1:0xXJJqHByHRNNuHnMgo2o0Ys6/HcCqoEO6Ibcaft/VQ= +inet.af/netaddr v0.0.0-20210129185718-d0669448cef6/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/netconf/chrony.go b/internal/netconf/chrony.go index 19d1c04..f07638a 100644 --- a/internal/netconf/chrony.go +++ b/internal/netconf/chrony.go @@ -27,15 +27,28 @@ func (c ChronyServiceEnabler) Enable() error { } func getDefaultRouteVRFName(kb KnowledgeBase) (string, error) { - networks := kb.GetNetworks(mn.External) - for _, network := range networks { - for _, prefix := range network.Destinationprefixes { - if prefix == AllZerosCIDR { - vrf := fmt.Sprintf("vrf%d", *network.Vrf) - return vrf, nil - } + externalNets := kb.GetNetworks(mn.External) + for _, network := range externalNets { + if containsDefaultRoute(network.Destinationprefixes) { + return vrfNameOf(network), nil + } + } + + privateSecondarySharedNets := kb.GetNetworks(mn.PrivateSecondaryShared) + for _, network := range privateSecondarySharedNets { + if containsDefaultRoute(network.Destinationprefixes) { + return vrfNameOf(network), nil } } return "", fmt.Errorf("there is no network providing a default (0.0.0.0/0) route") } + +func containsDefaultRoute(prefixes []string) bool { + for _, prefix := range prefixes { + if prefix == IPv4ZeroCIDR || prefix == IPv6ZeroCIDR { + return true + } + } + return false +} diff --git a/internal/netconf/chrony_test.go b/internal/netconf/chrony_test.go index 49a8116..1a562e9 100644 --- a/internal/netconf/chrony_test.go +++ b/internal/netconf/chrony_test.go @@ -13,7 +13,7 @@ func TestChronyServiceEnabler_Enable(t *testing.T) { vrf := int64(104009) external := mn.External - network := models.V1MachineNetwork{Networktype: &external, Destinationprefixes: []string{AllZerosCIDR}, Vrf: &vrf} + network := models.V1MachineNetwork{Networktype: &external, Destinationprefixes: []string{IPv4ZeroCIDR}, Vrf: &vrf} tests := []struct { kb KnowledgeBase vrf string diff --git a/internal/netconf/configurator.go b/internal/netconf/configurator.go index c987419..96223fe 100644 --- a/internal/netconf/configurator.go +++ b/internal/netconf/configurator.go @@ -95,14 +95,10 @@ func (configurator FirewallConfigurator) Configure() { kb := configurator.Kb applyCommonConfiguration(Firewall, kb) - src := mustTmpFile("rules.v4_") - validatorIPv4 := NftablesV4Validator{NftablesValidator{src}} - applier := NewNftablesConfigApplier(configurator.Kb, validatorIPv4) - applyAndCleanUp(applier, TplNftablesV4, src, "/etc/nftables/rules.v4", FileModeDefault) - src = mustTmpFile("rules.v6_") - validatorIPv6 := NftablesV6Validator{NftablesValidator{src}} - applier = NewNftablesConfigApplier(configurator.Kb, validatorIPv6) - applyAndCleanUp(applier, TplNftablesV6, src, "/etc/nftables/rules.v6", FileModeDefault) + src := mustTmpFile("nftrules_") + validator := NftablesValidator{src} + applier := NewNftablesConfigApplier(configurator.Kb, validator) + applyAndCleanUp(applier, TplNftables, src, "/etc/nftables/rules", FileModeDefault) chrony, err := NewChronyServiceEnabler(configurator.Kb) if err != nil { diff --git a/internal/netconf/frr.go b/internal/netconf/frr.go index d855747..352ae99 100644 --- a/internal/netconf/frr.go +++ b/internal/netconf/frr.go @@ -2,13 +2,12 @@ package netconf import ( "fmt" - "strconv" - "strings" "github.com/metal-stack/metal-go/api/models" mn "github.com/metal-stack/metal-lib/pkg/net" "github.com/metal-stack/metal-networker/pkg/exec" "github.com/metal-stack/metal-networker/pkg/net" + "inet.af/netaddr" ) const ( @@ -24,6 +23,10 @@ const ( IPPrefixListNoExportSuffix = "-no-export" // RouteMapOrderSeed defines the initial value for route-map order. RouteMapOrderSeed = 10 + // AddressFamilyIPv4 is the name for this address family for the routing daemon. + AddressFamilyIPv4 = "ip" + // AddressFamilyIPv6 is the name for this address family for the routing daemon. + AddressFamilyIPv6 = "ipv6" ) type ( @@ -51,6 +54,9 @@ type ( FRRValidator struct { path string } + + // AddressFamily is the address family for the routing daemon. + AddressFamily string ) // NewFrrConfigApplier constructs a new Applier of the given type of Bare Metal. @@ -61,13 +67,25 @@ func NewFrrConfigApplier(kind BareMetalType, kb KnowledgeBase, tmpFile string) n case Firewall: net := kb.getUnderlayNetwork() data = FirewallFRRData{ - CommonFRRData: newCommonFRRData(net, kb), - VRFs: assembleVRFs(kb), + CommonFRRData: CommonFRRData{ + FRRVersion: FRRVersion, + Hostname: kb.Hostname, + Comment: versionHeader(kb.Machineuuid), + ASN: *net.Asn, + RouterID: routerID(net), + }, + VRFs: assembleVRFs(kb), } case Machine: net := kb.getPrivatePrimaryNetwork() data = MachineFRRData{ - CommonFRRData: newCommonFRRData(net, kb), + CommonFRRData: CommonFRRData{ + FRRVersion: FRRVersion, + Hostname: kb.Hostname, + Comment: versionHeader(kb.Machineuuid), + ASN: *net.Asn, + RouterID: routerID(net), + }, } default: log.Fatalf("unknown kind of bare metal: %v", kind) @@ -78,9 +96,21 @@ func NewFrrConfigApplier(kind BareMetalType, kb KnowledgeBase, tmpFile string) n return net.NewNetworkApplier(data, validator, nil) } -func newCommonFRRData(net models.V1MachineNetwork, kb KnowledgeBase) CommonFRRData { - return CommonFRRData{FRRVersion: FRRVersion, Hostname: kb.Hostname, Comment: versionHeader(kb.Machineuuid), - ASN: *net.Asn, RouterID: net.Ips[0]} +// routerID will calculate the bgp router-id which must only be specified in the ipv6 range. +// returns 0.0.0.0 for errornous ip addresses and 169.254.255.255 for ipv6 +// TODO prepare machine allocations with ipv6 primary address and tests +func routerID(net models.V1MachineNetwork) string { + if len(net.Ips) < 1 { + return "0.0.0.0" + } + ip, err := netaddr.ParseIP(net.Ips[0]) + if err != nil { + return "0.0.0.0" + } + if ip.Is4() { + return ip.String() + } + return "169.254.255.255" } // Validate can be used to run validation on FRR configuration using vtysh. @@ -91,195 +121,27 @@ func (v FRRValidator) Validate() error { return exec.NewVerboseCmd("bash", "-c", vtysh, v.path).Run() } -func getDestinationPrefixes(networks []models.V1MachineNetwork) []string { - var result []string - for _, network := range networks { - result = append(result, network.Destinationprefixes...) - } - - return result -} - -func getPrefixes(networks ...models.V1MachineNetwork) []string { - var result []string - for _, network := range networks { - result = append(result, network.Prefixes...) - } - - return result -} - func assembleVRFs(kb KnowledgeBase) []VRF { var result []VRF - privatePrimary := kb.getPrivatePrimaryNetwork() networks := kb.GetNetworks(mn.PrivatePrimaryUnshared, mn.PrivatePrimaryShared, mn.PrivateSecondaryShared, mn.External) - for _, network := range networks { - var targets []models.V1MachineNetwork - var prefixes []string - if network.Networktype == nil { continue } - nt := *network.Networktype - switch nt { - case mn.PrivatePrimaryUnshared: - fallthrough - case mn.PrivatePrimaryShared: - // reach out from private primary network into public networks - publicTargets := kb.GetNetworks(mn.External) - prefixes = getDestinationPrefixes(publicTargets) - targets = append(targets, publicTargets...) - // reach out from private primary network into shared private networks - privateSharedTargets := kb.GetNetworks(mn.PrivateSecondaryShared) - prefixes = append(prefixes, getPrefixes(privateSharedTargets...)...) - targets = append(targets, privateSharedTargets...) - case mn.PrivateSecondaryShared: - // reach out from private shared networks into private primary network - targets = []models.V1MachineNetwork{privatePrimary} - prefixes = getPrefixes(append(targets, network)...) - case mn.External: - // reach out from public into private and other public networks - targets = []models.V1MachineNetwork{privatePrimary} - prefixes = getPrefixes(append(targets, network)...) - } - shared := (nt == mn.PrivatePrimaryShared || nt == mn.PrivateSecondaryShared) - vrfID := network.Vrf - vrfName := "vrf" + strconv.Itoa(int(*vrfID)) - prefixLists := assembleIPPrefixListsFor(vrfName, prefixes, IPPrefixListSeqSeed, kb, shared) + i := importRulesForNetwork(kb, network) vrf := VRF{ Identity: Identity{ ID: int(*network.Vrf), }, VNI: int(*network.Vrf), - ImportVRFNames: vrfNamesOf(targets...), - IPPrefixLists: prefixLists, - RouteMaps: assembleRouteMapsFor(vrfName, prefixLists), + ImportVRFNames: i.importVRFs, + IPPrefixLists: i.prefixLists(), + RouteMaps: i.routeMaps(), } result = append(result, vrf) } return result } - -func uniqueNames(prefixLists []IPPrefixList) []string { - var result []string - - uniqueNames := make(map[string]struct{}) - - for _, prefixList := range prefixLists { - if _, isPresent := uniqueNames[prefixList.Name]; isPresent { - continue - } - - uniqueNames[prefixList.Name] = struct{}{} - - result = append(result, prefixList.Name) - } - - return result -} - -func assembleRouteMapsFor(vrfName string, prefixLists []IPPrefixList) []RouteMap { - var result []RouteMap - - order := RouteMapOrderSeed - prefListNames := uniqueNames(prefixLists) - - for _, prefListName := range prefListNames { - entries := []string{"match ip address prefix-list " + prefListName} - if strings.HasSuffix(prefListName, IPPrefixListNoExportSuffix) { - entries = append(entries, "set community additive no-export") - } - - routeMap := RouteMap{ - Name: routeMapName(vrfName), - Policy: Permit.String(), - Order: order, - Entries: entries, - } - order += RouteMapOrderSeed - - result = append(result, routeMap) - } - - routeMap := RouteMap{ - Name: routeMapName(vrfName), - Policy: Deny.String(), - Order: order, - } - - result = append(result, routeMap) - - return result -} - -func routeMapName(vrfName string) string { - return vrfName + "-import-map" -} - -func vrfNamesOf(networks ...models.V1MachineNetwork) []string { - var result []string - - for _, n := range networks { - vrf := fmt.Sprintf("vrf%d", *n.Vrf) - result = append(result, vrf) - } - - return result -} - -func buildIPPrefixListSpecs(seq int, prefix string) []string { - var result []string - - spec := fmt.Sprintf("seq %d %s %s", seq, Permit, prefix) - if !strings.HasSuffix(prefix, "/0") { - spec += " le 32" - } - - result = append(result, spec) - - return result -} - -func assembleIPPrefixListsFor(vrfName string, prefixes []string, seed int, kb KnowledgeBase, shared bool) []IPPrefixList { - var result []IPPrefixList - - private := kb.getPrivatePrimaryNetwork() - - for _, prefix := range prefixes { - if len(prefix) == 0 { - continue - } - - specs := buildIPPrefixListSpecs(seed, prefix) - - for _, spec := range specs { - name := namePrefixList(vrfName, private, prefix, shared) - prefixList := IPPrefixList{ - Name: name, - Spec: spec, - } - result = append(result, prefixList) - } - - seed += len(specs) - } - - return result -} - -func namePrefixList(vrfName string, private models.V1MachineNetwork, prefix string, shared bool) string { - name := vrfName + "-import-prefixes" - - for _, privatePrefix := range private.Prefixes { - if privatePrefix == prefix && !shared { - // tenant private network ip addresses must not be visible in the public VRFs to avoid blown up routing tables - name += IPPrefixListNoExportSuffix - } - } - - return name -} diff --git a/internal/netconf/frr_test.go b/internal/netconf/frr_test.go index 1991a0b..82136b8 100644 --- a/internal/netconf/frr_test.go +++ b/internal/netconf/frr_test.go @@ -30,6 +30,27 @@ func TestFrrConfigApplier(t *testing.T) { configuratorType: Firewall, tpl: TplFirewallFRR, }, + { + name: "standard firewall with private primary unshared network, private secondary shared dmz network, internet and mpls", + input: "testdata/firewall_dmz.yaml", + expectedOutput: "testdata/frr.conf.firewall_dmz", + configuratorType: Firewall, + tpl: TplFirewallFRR, + }, + { + name: "standard firewall with private primary unshared network, private secondary shared dmz network", + input: "testdata/firewall_dmz_app.yaml", + expectedOutput: "testdata/frr.conf.firewall_dmz_app", + configuratorType: Firewall, + tpl: TplFirewallFRR, + }, + { + name: "firewall with private primary unshared ipv6 network, private secondary shared ipv4 network, ipv6 internet and ipv4 mpls", + input: "testdata/firewall_ipv6.yaml", + expectedOutput: "testdata/frr.conf.firewall_ipv6", + configuratorType: Firewall, + tpl: TplFirewallFRR, + }, { name: "standard machine", input: "testdata/machine.yaml", diff --git a/internal/netconf/interfaces.go b/internal/netconf/interfaces.go index c0f7411..dee1655 100644 --- a/internal/netconf/interfaces.go +++ b/internal/netconf/interfaces.go @@ -6,6 +6,7 @@ import ( "text/template" mn "github.com/metal-stack/metal-lib/pkg/net" + "inet.af/netaddr" ) type ( @@ -40,14 +41,14 @@ func NewIfacesApplier(kind BareMetalType, kb KnowledgeBase) IfacesApplier { case Firewall: underlay := kb.getUnderlayNetwork() d.Loopback.Comment = fmt.Sprintf("# networkid: %s", *underlay.Networkid) - d.Loopback.IPs = underlay.Ips + d.Loopback.IPs = addBitlen(underlay.Ips) d.EVPNIfaces = getEVPNIfaces(kb) case Machine: private := kb.getPrivatePrimaryNetwork() d.Loopback.Comment = fmt.Sprintf("# networkid: %s", *private.Networkid) // Ensure that the ips of the private network are the first ips at the loopback interface. // The first lo IP is used within network communication and other systems depend on seeing the first private ip. - d.Loopback.IPs = append(private.Ips, kb.CollectIPs(mn.External)...) + d.Loopback.IPs = addBitlen(append(private.Ips, kb.CollectIPs(mn.External)...)) default: log.Fatalf("unknown configuratorType of configurator: %v", kind) } @@ -55,6 +56,19 @@ func NewIfacesApplier(kind BareMetalType, kb KnowledgeBase) IfacesApplier { return IfacesApplier{kind: kind, kb: kb, data: d} } +func addBitlen(ips []string) []string { + ipsWithMask := []string{} + for _, ip := range ips { + parsedIP, err := netaddr.ParseIP(ip) + if err != nil { + continue + } + ipWithMask := fmt.Sprintf("%s/%d", ip, parsedIP.BitLen()) + ipsWithMask = append(ipsWithMask, ipWithMask) + } + return ipsWithMask +} + // Render renders the network interfaces to the given writer using the given template. func (a *IfacesApplier) Render(w io.Writer, tpl template.Template) error { return tpl.Execute(w, a.data) @@ -138,7 +152,7 @@ func getEVPNIfaces(kb KnowledgeBase) []EVPNIface { e.Comment = versionHeader(kb.Machineuuid) e.SVI.Comment = fmt.Sprintf("# svi (networkid: %s)", *n.Networkid) e.SVI.VLANID = VLANOffset + i - e.SVI.Addresses = n.Ips + e.SVI.Addresses = addBitlen(n.Ips) e.VXLAN.Comment = fmt.Sprintf("# vxlan (networkid: %s)", *n.Networkid) e.VXLAN.ID = vrf e.VXLAN.TunnelIP = kb.getUnderlayNetwork().Ips[0] diff --git a/internal/netconf/knowledgebase.go b/internal/netconf/knowledgebase.go index e1d16bb..2407904 100644 --- a/internal/netconf/knowledgebase.go +++ b/internal/netconf/knowledgebase.go @@ -128,7 +128,15 @@ func (kb KnowledgeBase) Validate(kind BareMetalType) error { } func (kb KnowledgeBase) containsAnyPublicNetwork() bool { - return len(kb.GetNetworks(mn.External)) > 0 + if len(kb.GetNetworks(mn.External)) > 0 { + return true + } + for _, n := range kb.Networks { + if isDMZNetwork(n) { + return true + } + } + return false } func (kb KnowledgeBase) containsSinglePrivatePrimary() bool { diff --git a/internal/netconf/knowledgebase_test.go b/internal/netconf/knowledgebase_test.go index 2d1601c..3ea4d82 100644 --- a/internal/netconf/knowledgebase_test.go +++ b/internal/netconf/knowledgebase_test.go @@ -50,7 +50,7 @@ func TestNewKnowledgeBase(t *testing.T) { // public networks n = d.Networks[2] assert.Equal(1, len(n.Destinationprefixes)) - assert.Equal(AllZerosCIDR, n.Destinationprefixes[0]) + assert.Equal(IPv4ZeroCIDR, n.Destinationprefixes[0]) assert.Equal(1, len(n.Ips)) assert.Equal("185.1.2.3", n.Ips[0]) assert.Equal(2, len(n.Prefixes)) diff --git a/internal/netconf/netobjects.go b/internal/netconf/netobjects.go index ab9d664..5bef87b 100644 --- a/internal/netconf/netobjects.go +++ b/internal/netconf/netobjects.go @@ -1,8 +1,11 @@ package netconf const ( - // AllZerosCIDR represents a CIDR notation that matches all addresses in the IPv4 address space. - AllZerosCIDR = "0.0.0.0/0" + // IPv4ZeroCIDR is the CIDR block for the whole IPv4 address space + IPv4ZeroCIDR = "0.0.0.0/0" + + // IPv6ZeroCIDR is the CIDR block for the whole IPv6 address space + IPv6ZeroCIDR = "::/0" // Permit defines an access policy that allows access. Permit AccessPolicy = iota // Deny defines an access policy that forbids access. @@ -45,8 +48,9 @@ type ( // IPPrefixList represents 'ip prefix-list' filtering mechanism to be used in combination with route-maps. IPPrefixList struct { - Name string - Spec string + Name string + Spec string + AddressFamily AddressFamily } // SVI represents a switched virtual interface. diff --git a/internal/netconf/nftables.go b/internal/netconf/nftables.go index 41c0923..0254cba 100644 --- a/internal/netconf/nftables.go +++ b/internal/netconf/nftables.go @@ -3,7 +3,9 @@ package netconf import ( "fmt" + "github.com/metal-stack/metal-go/api/models" "github.com/metal-stack/metal-networker/pkg/exec" + "inet.af/netaddr" "github.com/metal-stack/v" @@ -12,10 +14,9 @@ import ( mn "github.com/metal-stack/metal-lib/pkg/net" ) -// TplNftablesV4 defines the name of the template to render nftables configuration. const ( - TplNftablesV4 = "rules.v4.tpl" - TplNftablesV6 = "rules.v6.tpl" + // TplNftables defines the name of the template to render nftables configuration. + TplNftables = "nftrules.tpl" ) type ( @@ -29,23 +30,17 @@ type ( SNAT struct { Comment string OutInterface string - SourceSpecs []string + SourceSpecs []SourceSpec } + SourceSpec struct { + AddressFamily string + Source string + } // NftablesValidator can validate configuration for nftables rules. NftablesValidator struct { path string } - - // NftablesV4Validator can validate configuration for ipv4 nftables rules. - NftablesV4Validator struct { - NftablesValidator - } - - // NftablesV6Validator can validate configuration for ipv6 nftables rules. - NftablesV6Validator struct { - NftablesValidator - } ) // NewNftablesConfigApplier constructs a new instance of this type. @@ -59,22 +54,47 @@ func NewNftablesConfigApplier(kb KnowledgeBase, validator net.Validator) net.App return net.NewNetworkApplier(data, validator, nil) } +func isDMZNetwork(n models.V1MachineNetwork) bool { + return *n.Networktype == mn.PrivateSecondaryShared && containsDefaultRoute(n.Destinationprefixes) +} + func getSNAT(kb KnowledgeBase) []SNAT { var result []SNAT private := kb.getPrivatePrimaryNetwork() networks := kb.GetNetworks(mn.PrivatePrimaryUnshared, mn.PrivatePrimaryShared, mn.PrivateSecondaryShared, mn.External) + privatePfx := private.Prefixes + for _, n := range kb.Networks { + if isDMZNetwork(n) { + privatePfx = append(privatePfx, n.Prefixes...) + } + } + for _, n := range networks { if n.Nat != nil && !*n.Nat { continue } - var sources []string - sources = append(sources, private.Prefixes...) + var sources []SourceSpec cmt := fmt.Sprintf("snat (networkid: %s)", *n.Networkid) svi := fmt.Sprintf("vlan%d", *n.Vrf) + for _, p := range privatePfx { + ipprefix, err := netaddr.ParseIPPrefix(p) + if err != nil { + continue + } + af := "ip" + if ipprefix.IP.Is6() { + af = "ip6" + } + sspec := SourceSpec{ + Source: p, + AddressFamily: af, + } + sources = append(sources, sspec) + } s := SNAT{ Comment: cmt, OutInterface: svi, @@ -87,13 +107,7 @@ func getSNAT(kb KnowledgeBase) []SNAT { } // Validate validates network interfaces configuration. -func (v NftablesV4Validator) Validate() error { - log.Infof("running 'nft --check --file %s' to validate changes.", v.path) - return exec.NewVerboseCmd("nft", "--check", "--file", v.path).Run() -} - -// Validate validates network interfaces configuration. -func (v NftablesV6Validator) Validate() error { +func (v NftablesValidator) Validate() error { log.Infof("running 'nft --check --file %s' to validate changes.", v.path) return exec.NewVerboseCmd("nft", "--check", "--file", v.path).Run() } diff --git a/internal/netconf/nftables_test.go b/internal/netconf/nftables_test.go index 4dbabed..2e43207 100644 --- a/internal/netconf/nftables_test.go +++ b/internal/netconf/nftables_test.go @@ -12,26 +12,45 @@ func TestCompileNftRules(t *testing.T) { assert := assert.New(t) tests := []struct { + input string expected string - template string }{ - {expected: "testdata/nftrules.v4", template: TplNftablesV4}, - {expected: "testdata/nftrules.v6", template: TplNftablesV6}, + { + input: "testdata/firewall.yaml", + expected: "testdata/nftrules", + }, + { + input: "testdata/firewall_dmz.yaml", + expected: "testdata/nftrules_dmz", + }, + { + input: "testdata/firewall_dmz_app.yaml", + expected: "testdata/nftrules_dmz_app", + }, + { + input: "testdata/firewall_ipv6.yaml", + expected: "testdata/nftrules_ipv6", + }, + { + input: "testdata/firewall_shared.yaml", + expected: "testdata/nftrules_shared", + }, } - - for _, test := range tests { - expected, err := ioutil.ReadFile(test.expected) - assert.NoError(err) - - kb := NewKnowledgeBase("testdata/firewall.yaml") - assert.NoError(err) - - a := NewNftablesConfigApplier(kb, nil) - b := bytes.Buffer{} - - tpl := mustParseTpl(test.template) - err = a.Render(&b, *tpl) - assert.NoError(err) - assert.Equal(string(expected), b.String()) + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + expected, err := ioutil.ReadFile(tt.expected) + assert.NoError(err) + + kb := NewKnowledgeBase(tt.input) + assert.NoError(err) + + a := NewNftablesConfigApplier(kb, nil) + b := bytes.Buffer{} + + tpl := mustParseTpl(TplNftables) + err = a.Render(&b, *tpl) + assert.NoError(err) + assert.Equal(string(expected), b.String()) + }) } } diff --git a/internal/netconf/routemap.go b/internal/netconf/routemap.go new file mode 100644 index 0000000..e98dde0 --- /dev/null +++ b/internal/netconf/routemap.go @@ -0,0 +1,282 @@ +package netconf + +import ( + "fmt" + "sort" + "strings" + + "github.com/metal-stack/metal-go/api/models" + mn "github.com/metal-stack/metal-lib/pkg/net" + "inet.af/netaddr" +) + +type importRule struct { + targetVRF string + importVRFs []string + importPrefixes []netaddr.IPPrefix + importPrefixesNoExport []netaddr.IPPrefix +} + +func importRulesForNetwork(kb KnowledgeBase, network models.V1MachineNetwork) *importRule { + vrfName := vrfNameOf(network) + + if network.Networktype == nil || *network.Networktype == mn.Underlay { + return nil + } + i := importRule{ + targetVRF: vrfName, + } + privatePrimaryNet := kb.getPrivatePrimaryNetwork() + + externalNets := kb.GetNetworks(mn.External) + privateSecondarySharedNets := kb.GetNetworks(mn.PrivateSecondaryShared) + + nt := *network.Networktype + switch nt { + case mn.PrivatePrimaryUnshared: + fallthrough + case mn.PrivatePrimaryShared: + // reach out from private network into public networks + i.importVRFs = vrfNamesOf(externalNets) + i.importPrefixes = getDestinationPrefixes(externalNets) + + // reach out from private network into shared private networks + i.importVRFs = append(i.importVRFs, vrfNamesOf(privateSecondarySharedNets)...) + i.importPrefixes = append(i.importPrefixes, prefixesOfNetworks(privateSecondarySharedNets)...) + + // reach out from private network to destination prefixes of private secondays shared networks + for _, n := range privateSecondarySharedNets { + for _, pfx := range n.Destinationprefixes { + ppfx := netaddr.MustParseIPPrefix(pfx) + isThere := false + for _, i := range i.importPrefixes { + if i == ppfx { + isThere = true + } + } + if !isThere { + i.importPrefixes = append(i.importPrefixes, ppfx) + } + } + } + case mn.PrivateSecondaryShared: + // reach out from private shared networks into private primary network + i.importVRFs = []string{vrfNameOf(privatePrimaryNet)} + i.importPrefixes = concatPfxSlices(prefixesOfNetwork(privatePrimaryNet), prefixesOfNetwork(network)) + + // import destination prefixes of dmz networks from external networks + if len(network.Destinationprefixes) > 0 { + for _, pfx := range network.Destinationprefixes { + for _, e := range externalNets { + importExternalNet := false + for _, epfx := range e.Destinationprefixes { + if pfx == epfx { + importExternalNet = true + i.importPrefixes = append(i.importPrefixes, netaddr.MustParseIPPrefix(pfx)) + } + } + if importExternalNet { + i.importVRFs = append(i.importVRFs, vrfNameOf(e)) + } + } + } + } + case mn.External: + // reach out from public into private and other public networks + i.importVRFs = []string{vrfNameOf(privatePrimaryNet)} + i.importPrefixes = prefixesOfNetwork(network) + + nets := []models.V1MachineNetwork{privatePrimaryNet} + + if containsDefaultRoute(network.Destinationprefixes) { + for _, r := range privateSecondarySharedNets { + if containsDefaultRoute(r.Destinationprefixes) { + nets = append(nets, r) + i.importVRFs = append(i.importVRFs, vrfNameOf(r)) + } + } + } + i.importPrefixesNoExport = prefixesOfNetworks(nets) + } + + return &i +} + +func (i *importRule) prefixLists() []IPPrefixList { + var result []IPPrefixList + seed := IPPrefixListSeqSeed + afs := []AddressFamily{AddressFamilyIPv4, AddressFamilyIPv6} + for _, af := range afs { + pfxList := prefixLists(i.importPrefixesNoExport, af, false, seed, i.targetVRF) + result = append(result, pfxList...) + seed = IPPrefixListSeqSeed + len(pfxList) + result = append(result, prefixLists(i.importPrefixes, af, true, seed, i.targetVRF)...) + seed = IPPrefixListSeqSeed + } + + return result +} + +func prefixLists(prefixes []netaddr.IPPrefix, af AddressFamily, isExported bool, seed int, vrf string) []IPPrefixList { + var result []IPPrefixList + for _, prefix := range prefixes { + if af == AddressFamilyIPv4 && !prefix.IP.Is4() { + continue + } + + if af == AddressFamilyIPv6 && !prefix.IP.Is6() { + continue + } + + specs := buildIPPrefixListSpecs(seed, prefix) + for _, spec := range specs { + name := namePrefixList(vrf, prefix, isExported) + prefixList := IPPrefixList{ + Name: name, + Spec: spec, + AddressFamily: af, + } + result = append(result, prefixList) + } + seed++ + } + return result +} + +func concatPfxSlices(pfxSlices ...[]netaddr.IPPrefix) []netaddr.IPPrefix { + res := []netaddr.IPPrefix{} + for _, pfxSlice := range pfxSlices { + res = append(res, pfxSlice...) + } + return res +} + +func stringSliceToIPPrefix(s []string) []netaddr.IPPrefix { + var result []netaddr.IPPrefix + for _, e := range s { + ipp, err := netaddr.ParseIPPrefix(e) + if err != nil { + continue + } + result = append(result, ipp) + } + return result +} + +func getDestinationPrefixes(networks []models.V1MachineNetwork) []netaddr.IPPrefix { + var result []netaddr.IPPrefix + for _, network := range networks { + result = append(result, stringSliceToIPPrefix(network.Destinationprefixes)...) + } + return result +} + +func prefixesOfNetworks(networks []models.V1MachineNetwork) []netaddr.IPPrefix { + var result []netaddr.IPPrefix + for _, network := range networks { + result = append(result, prefixesOfNetwork(network)...) + } + return result +} + +func prefixesOfNetwork(network models.V1MachineNetwork) []netaddr.IPPrefix { + return stringSliceToIPPrefix(network.Prefixes) +} + +func vrfNameOf(n models.V1MachineNetwork) string { + return fmt.Sprintf("vrf%d", *n.Vrf) +} + +func vrfNamesOf(networks []models.V1MachineNetwork) []string { + var result []string + for _, n := range networks { + result = append(result, vrfNameOf(n)) + } + + return result +} + +func byName(prefixLists []IPPrefixList) map[string]IPPrefixList { + byName := map[string]IPPrefixList{} + for _, prefixList := range prefixLists { + if _, isPresent := byName[prefixList.Name]; isPresent { + continue + } + + byName[prefixList.Name] = prefixList + } + + return byName +} + +func (i *importRule) routeMaps() []RouteMap { + var result []RouteMap + + order := RouteMapOrderSeed + byName := byName(i.prefixLists()) + + names := []string{} + for n := range byName { + names = append(names, n) + } + sort.Sort(sort.Reverse(sort.StringSlice(names))) + + for _, n := range names { + prefixList := byName[n] + match := fmt.Sprintf("match %s address prefix-list %s", prefixList.AddressFamily, n) + entries := []string{match} + if strings.HasSuffix(n, IPPrefixListNoExportSuffix) { + entries = append(entries, "set community additive no-export") + } + + routeMap := RouteMap{ + Name: routeMapName(i.targetVRF), + Policy: Permit.String(), + Order: order, + Entries: entries, + } + order += RouteMapOrderSeed + + result = append(result, routeMap) + } + + routeMap := RouteMap{ + Name: routeMapName(i.targetVRF), + Policy: Deny.String(), + Order: order, + } + + result = append(result, routeMap) + + return result +} + +func routeMapName(vrfName string) string { + return vrfName + "-import-map" +} + +func buildIPPrefixListSpecs(seq int, prefix netaddr.IPPrefix) []string { + var result []string + + spec := fmt.Sprintf("seq %d %s %s", seq, Permit, prefix) + if prefix.Bits != 0 { + spec += fmt.Sprintf(" le %d", prefix.IP.BitLen()) + } + + result = append(result, spec) + + return result +} + +func namePrefixList(vrfName string, prefix netaddr.IPPrefix, isExported bool) string { + af := "" + if prefix.IP.Is6() { + af = "-ipv6" + } + export := "" + if !isExported { + export = IPPrefixListNoExportSuffix + } + + return fmt.Sprintf("%s-import-prefixes%s%s", vrfName, af, export) +} diff --git a/internal/netconf/routemap_test.go b/internal/netconf/routemap_test.go new file mode 100644 index 0000000..3cb6e49 --- /dev/null +++ b/internal/netconf/routemap_test.go @@ -0,0 +1,212 @@ +package netconf + +import ( + "fmt" + "reflect" + "testing" + + "inet.af/netaddr" +) + +type network struct { + vrf string + prefixes []netaddr.IPPrefix + destinations []netaddr.IPPrefix +} + +var ( + defaultRoute = netaddr.MustParseIPPrefix("0.0.0.0/0") + defaultRoute6 = netaddr.MustParseIPPrefix("::/0") + externalNet = netaddr.MustParseIPPrefix("100.127.129.0/24") + externalDestinationNet = netaddr.MustParseIPPrefix("100.127.1.0/24") + privateNet = netaddr.MustParseIPPrefix("10.0.16.0/22") + privateNet6 = netaddr.MustParseIPPrefix("2002::/64") + sharedNet = netaddr.MustParseIPPrefix("10.0.18.0/22") + dmzNet = netaddr.MustParseIPPrefix("10.0.20.0/22") + inetNet1 = netaddr.MustParseIPPrefix("185.1.2.0/24") + inetNet2 = netaddr.MustParseIPPrefix("185.27.0.0/22") + inetNet6 = netaddr.MustParseIPPrefix("2a02:c00:20::/45") + + private = network{ + vrf: "vrf3981", + prefixes: []netaddr.IPPrefix{privateNet}, + } + + private6 = network{ + vrf: "vrf3981", + prefixes: []netaddr.IPPrefix{privateNet6}, + } + + inet = network{ + vrf: "vrf104009", + prefixes: []netaddr.IPPrefix{inetNet1, inetNet2}, + destinations: []netaddr.IPPrefix{defaultRoute}, + } + + inet6 = network{ + vrf: "vrf104009", + prefixes: []netaddr.IPPrefix{inetNet6}, + destinations: []netaddr.IPPrefix{defaultRoute6}, + } + + external = network{ + vrf: "vrf104010", + destinations: []netaddr.IPPrefix{externalDestinationNet}, + prefixes: []netaddr.IPPrefix{externalNet}, + } + + shared = network{ + vrf: "vrf3982", + prefixes: []netaddr.IPPrefix{sharedNet}, + } + + dmz = network{ + vrf: "vrf3983", + prefixes: []netaddr.IPPrefix{dmzNet}, + destinations: []netaddr.IPPrefix{defaultRoute}, + } +) + +func Test_importRulesForNetwork(t *testing.T) { + tests := []struct { + name string + input string + want []*importRule + }{ + { + name: "standard firewall with private primary unshared network, private secondary shared network, internet and mpls", + input: "testdata/firewall.yaml", + want: []*importRule{ + { + targetVRF: private.vrf, + importVRFs: []string{inet.vrf, external.vrf, shared.vrf}, + importPrefixes: concatPfxSlices(inet.destinations, external.destinations, shared.prefixes), + }, + { + targetVRF: shared.vrf, + importVRFs: []string{private.vrf}, + importPrefixes: concatPfxSlices(private.prefixes, shared.prefixes), + }, + { + targetVRF: inet.vrf, + importVRFs: []string{private.vrf}, + importPrefixes: inet.prefixes, + importPrefixesNoExport: private.prefixes, + }, + nil, + { + targetVRF: external.vrf, + importVRFs: []string{private.vrf}, + importPrefixes: external.prefixes, + importPrefixesNoExport: private.prefixes, + }, + }, + }, + { + name: "firewall of a shared private network (shared/storage firewall)", + input: "testdata/firewall_shared.yaml", + want: []*importRule{ + { + targetVRF: shared.vrf, + importVRFs: []string{inet.vrf}, + importPrefixes: concatPfxSlices(inet.destinations), + }, + { + targetVRF: inet.vrf, + importVRFs: []string{shared.vrf}, + importPrefixes: concatPfxSlices(inet.prefixes), + importPrefixesNoExport: shared.prefixes, + }, + nil, + }, + }, + { + name: "firewall of a private network with dmz network and internet (dmz firewall)", + input: "testdata/firewall_dmz.yaml", + want: []*importRule{ + { + targetVRF: private.vrf, + importVRFs: []string{inet.vrf, dmz.vrf}, + importPrefixes: concatPfxSlices(inet.destinations, dmz.prefixes), + }, + { + targetVRF: dmz.vrf, + importVRFs: []string{private.vrf, inet.vrf}, + importPrefixes: concatPfxSlices(private.prefixes, dmz.prefixes, dmz.destinations), + }, + { + targetVRF: inet.vrf, + importVRFs: []string{private.vrf, dmz.vrf}, + importPrefixes: inet.prefixes, + importPrefixesNoExport: concatPfxSlices(private.prefixes, dmz.prefixes), + }, + nil, + }, + }, + { + name: "firewall of a private network with dmz network (dmz app firewall)", + input: "testdata/firewall_dmz_app.yaml", + want: []*importRule{ + { + targetVRF: private.vrf, + importVRFs: []string{dmz.vrf}, + importPrefixes: concatPfxSlices(dmz.prefixes, dmz.destinations), + }, + { + targetVRF: dmz.vrf, + importVRFs: []string{private.vrf}, + importPrefixes: concatPfxSlices(private.prefixes, dmz.prefixes), + }, + nil, + }, + }, + { + name: "firewall with ipv6 private network and ipv6 internet network", + input: "testdata/firewall_ipv6.yaml", + want: []*importRule{ + { + targetVRF: private6.vrf, + importVRFs: []string{inet.vrf, external.vrf, shared.vrf}, + importPrefixes: concatPfxSlices(inet6.destinations, external.destinations, shared.prefixes), + }, + { + targetVRF: shared.vrf, + importVRFs: []string{private6.vrf}, + importPrefixes: concatPfxSlices(private6.prefixes, shared.prefixes), + }, + { + targetVRF: inet6.vrf, + importVRFs: []string{private.vrf}, + importPrefixes: inet6.prefixes, + importPrefixesNoExport: private6.prefixes, + }, + nil, + { + targetVRF: external.vrf, + importVRFs: []string{private6.vrf}, + importPrefixes: external.prefixes, + importPrefixesNoExport: private6.prefixes, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kb := NewKnowledgeBase(tt.input) + err := kb.Validate(Firewall) + if err != nil { + t.Errorf("%s is not valid: %v", tt.input, err) + return + } + for i, network := range kb.Networks { + got := importRulesForNetwork(kb, network) + if !reflect.DeepEqual(got, tt.want[i]) { + fmt.Printf("g: %v\nw: %v\n", got.importPrefixes, tt.want[i].importPrefixes) + fmt.Printf("g: %v\nw: %v\n", got.importPrefixesNoExport, tt.want[i].importPrefixesNoExport) + t.Errorf("importRulesForNetwork() got %v, wanted %v", got, tt.want[i]) + } + } + }) + } +} diff --git a/internal/netconf/testdata/firewall_dmz.yaml b/internal/netconf/testdata/firewall_dmz.yaml new file mode 100644 index 0000000..cb7e76c --- /dev/null +++ b/internal/netconf/testdata/firewall_dmz.yaml @@ -0,0 +1,164 @@ +# Note: This is a general-purpose configuration file that contains information not only for this app. +# +# This file is considered to be used to configure the tenant firewall! +# +########################################### +# root@firewall:/etc/metal# date +# Thu May 16 13:48:11 CEST 2019 +# root@firewall:/etc/metal# cat install.yaml +# hostname: firewall +# ipaddress: 10.0.12.1 +# asn: "4200003073" +# networks: +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.0.12.1 +# nat: false +# networkid: bc830818-2df1-4904-8c40-4322296d393d +# prefixes: +# - 10.0.12.0/22 +# private: true +# underlay: false +# vrf: 3981 +# - asn: 4200003073 +# destinationprefixes: +# - 0.0.0.0/0 +# ips: +# - 185.24.0.1 +# nat: false +# networkid: internet-vagrant-lab +# prefixes: +# - 185.24.0.0/22 +# - 185.27.0.0/22 +# private: false +# underlay: false +# vrf: 104009 +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.1.0.1 +# nat: false +# networkid: underlay-vagrant-lab +# prefixes: +# - 10.0.12.0/22 +# private: false +# underlay: true +# vrf: 0 +# machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# sshpublickey: "" +# password: KAWT5DugqSPAezMl +# devmode: false +# console: ttyS0,115200n8 +########################################### +--- +# Applies to hostname of the firewall. +hostname: firewall +networks: + # === Tenant Network (private=true) + # [IGNORED] + - asn: 4200003073 + # [IGNORED in case of private network] + destinationprefixes: [] + # For Firewall: Used to consider the set of prefixes that originate the given IP's to establish route leak in public + # network VRF's for return traffic. Applied to the SVI (as /32) + # For Machine: Used to set the loopback ips. + ips: + - 10.0.16.2 + # [IGNORED in case of private network] + nat: false + # [IGNORED in case of private network] + networkid: bc830818-2df1-4904-8c40-4322296d393d + # considered as source range for nat and to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 10.0.16.0/22 + private: true + underlay: false + networktype: privateprimaryunshared + # [IGNORED in case of private network] + # Defines the tenant VRF id. + vrf: 3981 + - asn: 4200003073 + destinationprefixes: + - 0.0.0.0/0 + # Applied to the SVI (as /32) + ips: + - 10.0.20.2 + # In case nat equals true, Source NAT via SVI is added. + nat: false + networkid: dmz-net + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 10.0.20.0/22 + private: true + underlay: false + privateprimary: false + networktype: privatesecondaryshared + # VRF id considered to define EVPN interfaces. + vrf: 3983 + # === Public networks to route to + # [IGNORED] + - asn: 4200003073 + # Considered to establish static route leak to reach out from tenant VRF into the public networks. + destinationprefixes: + - 0.0.0.0/0 + # Applied to the SVI (as /32) + ips: + - 185.1.2.3 + # In case nat equals true, Source NAT via SVI is added. + nat: true + networkid: internet-vagrant-lab + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 185.1.2.0/24 + - 185.27.0.0/22 + private: false + underlay: false + networktype: external + # VRF id considered to define EVPN interfaces. + vrf: 104009 + # === Underlay Network (underlay=true) + # Considered to define the BGP ASN. + - asn: 4200003073 + # Considered to establish static route leak to reach out from tenant VRF into the public networks. + destinationprefixes: [] + # Applied to local loopback as /32. + ips: + - 10.1.0.1 + nat: false + networkid: underlay-vagrant-lab + # [IGNORED in case of UNDERLAY] + prefixes: + - 10.0.12.0/22 + private: false + underlay: true + networktype: underlay + # [IGNORED] Underlay runs in default VRF. + vrf: 0 +machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# [IGNORED] +sshpublickey: "" +# [IGNORED] +password: KAWT5DugqSPAezMl +# [IGNORED] +devmode: false +# [IGNORED] +console: ttyS1,115200n8 +timestamp: "2019-07-01T09:41:43Z" +nics: + - mac: "00:03:00:11:11:01" + name: lan0 + neighbors: + - mac: 44:38:39:00:00:1a + name: null + neighbors: [] + - mac: "00:03:00:11:12:01" + name: lan1 + neighbors: + - mac: "44:38:39:00:00:04" + name: null + neighbors: [] + + + + diff --git a/internal/netconf/testdata/firewall_dmz_app.yaml b/internal/netconf/testdata/firewall_dmz_app.yaml new file mode 100644 index 0000000..414ece6 --- /dev/null +++ b/internal/netconf/testdata/firewall_dmz_app.yaml @@ -0,0 +1,141 @@ +# Note: This is a general-purpose configuration file that contains information not only for this app. +# +# This file is considered to be used to configure the tenant firewall! +# +########################################### +# root@firewall:/etc/metal# date +# Thu May 16 13:48:11 CEST 2019 +# root@firewall:/etc/metal# cat install.yaml +# hostname: firewall +# ipaddress: 10.0.12.1 +# asn: "4200003073" +# networks: +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.0.12.1 +# nat: false +# networkid: bc830818-2df1-4904-8c40-4322296d393d +# prefixes: +# - 10.0.12.0/22 +# private: true +# underlay: false +# vrf: 3981 +# - asn: 4200003073 +# destinationprefixes: +# - 0.0.0.0/0 +# ips: +# - 185.24.0.1 +# nat: false +# networkid: internet-vagrant-lab +# prefixes: +# - 185.24.0.0/22 +# - 185.27.0.0/22 +# private: false +# underlay: false +# vrf: 104009 +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.1.0.1 +# nat: false +# networkid: underlay-vagrant-lab +# prefixes: +# - 10.0.12.0/22 +# private: false +# underlay: true +# vrf: 0 +# machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# sshpublickey: "" +# password: KAWT5DugqSPAezMl +# devmode: false +# console: ttyS0,115200n8 +########################################### +--- +# Applies to hostname of the firewall. +hostname: firewall +networks: + # === Tenant Network (private=true) + # [IGNORED] + - asn: 4200003073 + # [IGNORED in case of private network] + destinationprefixes: [] + # For Firewall: Used to consider the set of prefixes that originate the given IP's to establish route leak in public + # network VRF's for return traffic. Applied to the SVI (as /32) + # For Machine: Used to set the loopback ips. + ips: + - 10.0.16.2 + # [IGNORED in case of private network] + nat: false + # [IGNORED in case of private network] + networkid: bc830818-2df1-4904-8c40-4322296d393d + # considered as source range for nat and to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 10.0.16.0/22 + private: true + underlay: false + networktype: privateprimaryunshared + # [IGNORED in case of private network] + # Defines the tenant VRF id. + vrf: 3981 + - asn: 4200003073 + destinationprefixes: + - 0.0.0.0/0 + # Applied to the SVI (as /32) + ips: + - 10.0.20.2 + # In case nat equals true, Source NAT via SVI is added. + nat: false + networkid: dmz-net + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 10.0.20.0/22 + private: true + underlay: false + privateprimary: false + networktype: privatesecondaryshared + # VRF id considered to define EVPN interfaces. + vrf: 3983 + - asn: 4200003073 + # Considered to establish static route leak to reach out from tenant VRF into the public networks. + destinationprefixes: [] + # Applied to local loopback as /32. + ips: + - 10.1.0.1 + nat: false + networkid: underlay-vagrant-lab + # [IGNORED in case of UNDERLAY] + prefixes: + - 10.0.12.0/22 + private: false + underlay: true + networktype: underlay + # [IGNORED] Underlay runs in default VRF. + vrf: 0 +machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# [IGNORED] +sshpublickey: "" +# [IGNORED] +password: KAWT5DugqSPAezMl +# [IGNORED] +devmode: false +# [IGNORED] +console: ttyS1,115200n8 +timestamp: "2019-07-01T09:41:43Z" +nics: + - mac: "00:03:00:11:11:01" + name: lan0 + neighbors: + - mac: 44:38:39:00:00:1a + name: null + neighbors: [] + - mac: "00:03:00:11:12:01" + name: lan1 + neighbors: + - mac: "44:38:39:00:00:04" + name: null + neighbors: [] + + + + diff --git a/internal/netconf/testdata/firewall_ipv6.yaml b/internal/netconf/testdata/firewall_ipv6.yaml new file mode 100644 index 0000000..6f9aec1 --- /dev/null +++ b/internal/netconf/testdata/firewall_ipv6.yaml @@ -0,0 +1,181 @@ +# Note: This is a general-purpose configuration file that contains information not only for this app. +# +# This file is considered to be used to configure the tenant firewall! +# +########################################### +# root@firewall:/etc/metal# date +# Thu May 16 13:48:11 CEST 2019 +# root@firewall:/etc/metal# cat install.yaml +# hostname: firewall +# ipaddress: 10.0.12.1 +# asn: "4200003073" +# networks: +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.0.12.1 +# nat: false +# networkid: bc830818-2df1-4904-8c40-4322296d393d +# prefixes: +# - 10.0.12.0/22 +# private: true +# underlay: false +# vrf: 3981 +# - asn: 4200003073 +# destinationprefixes: +# - 0.0.0.0/0 +# ips: +# - 185.24.0.1 +# nat: false +# networkid: internet-vagrant-lab +# prefixes: +# - 185.24.0.0/22 +# - 185.27.0.0/22 +# private: false +# underlay: false +# vrf: 104009 +# - asn: 4200003073 +# destinationprefixes: [] +# ips: +# - 10.1.0.1 +# nat: false +# networkid: underlay-vagrant-lab +# prefixes: +# - 10.0.12.0/22 +# private: false +# underlay: true +# vrf: 0 +# machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# sshpublickey: "" +# password: KAWT5DugqSPAezMl +# devmode: false +# console: ttyS0,115200n8 +########################################### +--- +# Applies to hostname of the firewall. +hostname: firewall +networks: + # === Tenant Network (private=true) + # [IGNORED] + - asn: 4200003073 + # [IGNORED in case of private network] + destinationprefixes: [] + # For Firewall: Used to consider the set of prefixes that originate the given IP's to establish route leak in public + # network VRF's for return traffic. Applied to the SVI (as /32) + # For Machine: Used to set the loopback ips. + ips: + - 2002::1 + # [IGNORED in case of private network] + nat: false + # [IGNORED in case of private network] + networkid: bc830818-2df1-4904-8c40-4322296d393d + # considered as source range for nat and to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 2002::/64 + private: true + underlay: false + networktype: privateprimaryunshared + # [IGNORED in case of private network] + # Defines the tenant VRF id. + vrf: 3981 + # === Private shared networks to route to + # [IGNORED] + - asn: 4200003073 + # [IGNORED in case of private network] + destinationprefixes: [] + # Applied to the SVI (as /32) + ips: + - 10.0.18.2 + # In case nat equals true, Source NAT via SVI is added. + nat: false + networkid: storage-net + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 10.0.18.0/22 + private: true + underlay: false + networktype: privatesecondaryshared + # VRF id considered to define EVPN interfaces. + vrf: 3982 + # === Public networks to route to + # [IGNORED] + - asn: 4200003073 + # Considered to establish static route leak to reach out from tenant VRF into the public networks. + destinationprefixes: + - ::/0 + # Applied to the SVI (as /32) + ips: + - 2a02:c00:20::1 + # In case nat equals true, Source NAT via SVI is added. + nat: true + networkid: internet-vagrant-lab + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 2a02:c00:20::/45 + private: false + underlay: false + networktype: external + # VRF id considered to define EVPN interfaces. + vrf: 104009 + # === Underlay Network (underlay=true) + # Considered to define the BGP ASN. + - asn: 4200003073 + # Considered to establish static route leak to reach out from tenant VRF into the public networks. + destinationprefixes: [] + # Applied to local loopback as /32. + ips: + - 10.1.0.1 + nat: false + networkid: underlay-vagrant-lab + # [IGNORED in case of UNDERLAY] + prefixes: + - 10.0.12.0/22 + private: false + privateprimary: false + underlay: true + networktype: underlay + # [IGNORED] Underlay runs in default VRF. + vrf: 0 + - asn: 4200003073 + # considered to figure out allowed prefixes for route imports from public network into tenant network + destinationprefixes: + - 100.127.1.0/24 + # Applied to local loopback as /32. + ips: + - 100.127.129.1 + nat: true + networkid: mpls-nbg-w8101-test + # considered to figure out allowed prefixes for route imports from private network into non-private, non-underlay network + prefixes: + - 100.127.129.0/24 + private: false + underlay: false + networktype: external + vrf: 104010 +machineuuid: e0ab02d2-27cd-5a5e-8efc-080ba80cf258 +# [IGNORED] +sshpublickey: "" +# [IGNORED] +password: KAWT5DugqSPAezMl +# [IGNORED] +devmode: false +# [IGNORED] +console: ttyS1,115200n8 +timestamp: "2019-07-01T09:41:43Z" +nics: + - mac: "00:03:00:11:11:01" + name: lan0 + neighbors: + - mac: 44:38:39:00:00:1a + name: null + neighbors: [] + - mac: "00:03:00:11:12:01" + name: lan1 + neighbors: + - mac: "44:38:39:00:00:04" + name: null + neighbors: [] + + + + diff --git a/internal/netconf/testdata/frr.conf.firewall b/internal/netconf/testdata/frr.conf.firewall index 90054da..bfdcdb3 100644 --- a/internal/netconf/testdata/frr.conf.firewall +++ b/internal/netconf/testdata/frr.conf.firewall @@ -47,6 +47,12 @@ router bgp 4200003073 neighbor FABRIC route-map only-self-out out exit-address-family ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! address-family l2vpn evpn neighbor FABRIC activate advertise-all-vni @@ -64,8 +70,17 @@ router bgp 4200003073 vrf vrf3981 import vrf route-map vrf3981-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf104009 + import vrf vrf104010 + import vrf vrf3982 + import vrf route-map vrf3981-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! router bgp 4200003073 vrf vrf3982 @@ -78,8 +93,15 @@ router bgp 4200003073 vrf vrf3982 import vrf route-map vrf3982-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf3982-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! router bgp 4200003073 vrf vrf104009 @@ -92,8 +114,15 @@ router bgp 4200003073 vrf vrf104009 import vrf route-map vrf104009-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104009-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! router bgp 4200003073 vrf vrf104010 @@ -106,8 +135,15 @@ router bgp 4200003073 vrf vrf104010 import vrf route-map vrf104010-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104010-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! ip prefix-list vrf3981-import-prefixes seq 100 permit 0.0.0.0/0 diff --git a/internal/netconf/testdata/frr.conf.firewall_dmz b/internal/netconf/testdata/frr.conf.firewall_dmz new file mode 100644 index 0000000..c21fafe --- /dev/null +++ b/internal/netconf/testdata/frr.conf.firewall_dmz @@ -0,0 +1,161 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +frr version 7.5 +frr defaults datacenter +hostname firewall +username cumulus nopassword +! +service integrated-vtysh-config +! +log syslog debugging +debug bgp updates +debug bgp nht +debug bgp update-groups +debug bgp zebra +! +vrf vrf3981 + vni 3981 +! +vrf vrf3983 + vni 3983 +! +vrf vrf104009 + vni 104009 +! +interface lan0 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +interface lan1 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +router bgp 4200003073 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + neighbor FABRIC peer-group + neighbor FABRIC remote-as external + neighbor FABRIC timers 1 3 + neighbor lan0 interface peer-group FABRIC + neighbor lan1 interface peer-group FABRIC + ! + address-family ipv4 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + exit-address-family + ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! + address-family l2vpn evpn + neighbor FABRIC activate + advertise-all-vni + exit-address-family +! +router bgp 4200003073 vrf vrf3981 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf104009 + import vrf vrf3983 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf104009 + import vrf vrf3983 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf3983 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf vrf104009 + import vrf route-map vrf3983-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf vrf104009 + import vrf route-map vrf3983-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf104009 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf vrf3983 + import vrf route-map vrf104009-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf vrf3983 + import vrf route-map vrf104009-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +ip prefix-list vrf3981-import-prefixes seq 100 permit 0.0.0.0/0 +ip prefix-list vrf3981-import-prefixes seq 101 permit 10.0.20.0/22 le 32 +route-map vrf3981-import-map permit 10 + match ip address prefix-list vrf3981-import-prefixes +route-map vrf3981-import-map deny 20 +! +ip prefix-list vrf3983-import-prefixes seq 100 permit 10.0.16.0/22 le 32 +ip prefix-list vrf3983-import-prefixes seq 101 permit 10.0.20.0/22 le 32 +ip prefix-list vrf3983-import-prefixes seq 102 permit 0.0.0.0/0 +route-map vrf3983-import-map permit 10 + match ip address prefix-list vrf3983-import-prefixes +route-map vrf3983-import-map deny 20 +! +ip prefix-list vrf104009-import-prefixes-no-export seq 100 permit 10.0.16.0/22 le 32 +ip prefix-list vrf104009-import-prefixes-no-export seq 101 permit 10.0.20.0/22 le 32 +ip prefix-list vrf104009-import-prefixes seq 102 permit 185.1.2.0/24 le 32 +ip prefix-list vrf104009-import-prefixes seq 103 permit 185.27.0.0/22 le 32 +route-map vrf104009-import-map permit 10 + match ip address prefix-list vrf104009-import-prefixes-no-export + set community additive no-export +route-map vrf104009-import-map permit 20 + match ip address prefix-list vrf104009-import-prefixes +route-map vrf104009-import-map deny 30 +! +route-map only-self-out permit 10 + match as-path SELF +route-map only-self-out deny 20 +! +route-map LOOPBACKS permit 10 + match interface lo +! +bgp as-path access-list SELF permit ^$ +! +line vty +! \ No newline at end of file diff --git a/internal/netconf/testdata/frr.conf.firewall_dmz_app b/internal/netconf/testdata/frr.conf.firewall_dmz_app new file mode 100644 index 0000000..bd871e4 --- /dev/null +++ b/internal/netconf/testdata/frr.conf.firewall_dmz_app @@ -0,0 +1,119 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +frr version 7.5 +frr defaults datacenter +hostname firewall +username cumulus nopassword +! +service integrated-vtysh-config +! +log syslog debugging +debug bgp updates +debug bgp nht +debug bgp update-groups +debug bgp zebra +! +vrf vrf3981 + vni 3981 +! +vrf vrf3983 + vni 3983 +! +interface lan0 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +interface lan1 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +router bgp 4200003073 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + neighbor FABRIC peer-group + neighbor FABRIC remote-as external + neighbor FABRIC timers 1 3 + neighbor lan0 interface peer-group FABRIC + neighbor lan1 interface peer-group FABRIC + ! + address-family ipv4 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + exit-address-family + ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! + address-family l2vpn evpn + neighbor FABRIC activate + advertise-all-vni + exit-address-family +! +router bgp 4200003073 vrf vrf3981 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3983 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3983 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf3983 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf3983-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf3983-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +ip prefix-list vrf3981-import-prefixes seq 100 permit 10.0.20.0/22 le 32 +ip prefix-list vrf3981-import-prefixes seq 101 permit 0.0.0.0/0 +route-map vrf3981-import-map permit 10 + match ip address prefix-list vrf3981-import-prefixes +route-map vrf3981-import-map deny 20 +! +ip prefix-list vrf3983-import-prefixes seq 100 permit 10.0.16.0/22 le 32 +ip prefix-list vrf3983-import-prefixes seq 101 permit 10.0.20.0/22 le 32 +route-map vrf3983-import-map permit 10 + match ip address prefix-list vrf3983-import-prefixes +route-map vrf3983-import-map deny 20 +! +route-map only-self-out permit 10 + match as-path SELF +route-map only-self-out deny 20 +! +route-map LOOPBACKS permit 10 + match interface lo +! +bgp as-path access-list SELF permit ^$ +! +line vty +! \ No newline at end of file diff --git a/internal/netconf/testdata/frr.conf.firewall_ipv6 b/internal/netconf/testdata/frr.conf.firewall_ipv6 new file mode 100644 index 0000000..92bd2f8 --- /dev/null +++ b/internal/netconf/testdata/frr.conf.firewall_ipv6 @@ -0,0 +1,194 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +frr version 7.5 +frr defaults datacenter +hostname firewall +username cumulus nopassword +! +service integrated-vtysh-config +! +log syslog debugging +debug bgp updates +debug bgp nht +debug bgp update-groups +debug bgp zebra +! +vrf vrf3981 + vni 3981 +! +vrf vrf3982 + vni 3982 +! +vrf vrf104009 + vni 104009 +! +vrf vrf104010 + vni 104010 +! +interface lan0 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +interface lan1 + ipv6 nd ra-interval 6 + no ipv6 nd suppress-ra +! +router bgp 4200003073 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + neighbor FABRIC peer-group + neighbor FABRIC remote-as external + neighbor FABRIC timers 1 3 + neighbor lan0 interface peer-group FABRIC + neighbor lan1 interface peer-group FABRIC + ! + address-family ipv4 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + exit-address-family + ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! + address-family l2vpn evpn + neighbor FABRIC activate + advertise-all-vni + exit-address-family +! +router bgp 4200003073 vrf vrf3981 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf104009 + import vrf vrf104010 + import vrf vrf3982 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf104009 + import vrf vrf104010 + import vrf vrf3982 + import vrf route-map vrf3981-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf3982 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf3982-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf3982-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf104009 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104009-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104009-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +router bgp 4200003073 vrf vrf104010 + bgp router-id 10.1.0.1 + bgp bestpath as-path multipath-relax + ! + address-family ipv4 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104010-import-map + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3981 + import vrf route-map vrf104010-import-map + exit-address-family + ! + address-family l2vpn evpn + advertise ipv4 unicast + advertise ipv6 unicast + exit-address-family +! +ip prefix-list vrf3981-import-prefixes seq 100 permit 100.127.1.0/24 le 32 +ip prefix-list vrf3981-import-prefixes seq 101 permit 10.0.18.0/22 le 32 +ipv6 prefix-list vrf3981-import-prefixes-ipv6 seq 100 permit ::/0 +route-map vrf3981-import-map permit 10 + match ipv6 address prefix-list vrf3981-import-prefixes-ipv6 +route-map vrf3981-import-map permit 20 + match ip address prefix-list vrf3981-import-prefixes +route-map vrf3981-import-map deny 30 +! +ip prefix-list vrf3982-import-prefixes seq 100 permit 10.0.18.0/22 le 32 +ipv6 prefix-list vrf3982-import-prefixes-ipv6 seq 100 permit 2002::/64 le 128 +route-map vrf3982-import-map permit 10 + match ipv6 address prefix-list vrf3982-import-prefixes-ipv6 +route-map vrf3982-import-map permit 20 + match ip address prefix-list vrf3982-import-prefixes +route-map vrf3982-import-map deny 30 +! +ipv6 prefix-list vrf104009-import-prefixes-ipv6-no-export seq 100 permit 2002::/64 le 128 +ipv6 prefix-list vrf104009-import-prefixes-ipv6 seq 101 permit 2a02:c00:20::/45 le 128 +route-map vrf104009-import-map permit 10 + match ipv6 address prefix-list vrf104009-import-prefixes-ipv6-no-export + set community additive no-export +route-map vrf104009-import-map permit 20 + match ipv6 address prefix-list vrf104009-import-prefixes-ipv6 +route-map vrf104009-import-map deny 30 +! +ip prefix-list vrf104010-import-prefixes seq 100 permit 100.127.129.0/24 le 32 +ipv6 prefix-list vrf104010-import-prefixes-ipv6-no-export seq 100 permit 2002::/64 le 128 +route-map vrf104010-import-map permit 10 + match ipv6 address prefix-list vrf104010-import-prefixes-ipv6-no-export + set community additive no-export +route-map vrf104010-import-map permit 20 + match ip address prefix-list vrf104010-import-prefixes +route-map vrf104010-import-map deny 30 +! +route-map only-self-out permit 10 + match as-path SELF +route-map only-self-out deny 20 +! +route-map LOOPBACKS permit 10 + match interface lo +! +bgp as-path access-list SELF permit ^$ +! +line vty +! \ No newline at end of file diff --git a/internal/netconf/testdata/frr.conf.firewall_shared b/internal/netconf/testdata/frr.conf.firewall_shared index 58ba8f2..6177db8 100644 --- a/internal/netconf/testdata/frr.conf.firewall_shared +++ b/internal/netconf/testdata/frr.conf.firewall_shared @@ -41,6 +41,12 @@ router bgp 4200003073 neighbor FABRIC route-map only-self-out out exit-address-family ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! address-family l2vpn evpn neighbor FABRIC activate advertise-all-vni @@ -56,8 +62,15 @@ router bgp 4200003073 vrf vrf3982 import vrf route-map vrf3982-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf104009 + import vrf route-map vrf3982-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! router bgp 4200003073 vrf vrf104009 @@ -70,8 +83,15 @@ router bgp 4200003073 vrf vrf104009 import vrf route-map vrf104009-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + import vrf vrf3982 + import vrf route-map vrf104009-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! ip prefix-list vrf3982-import-prefixes seq 100 permit 0.0.0.0/0 diff --git a/internal/netconf/testdata/frr.conf.machine b/internal/netconf/testdata/frr.conf.machine index 8711780..b7639da 100644 --- a/internal/netconf/testdata/frr.conf.machine +++ b/internal/netconf/testdata/frr.conf.machine @@ -42,6 +42,13 @@ router bgp 4200003073 redistribute kernel neighbor TOR route-map only-self-out out exit-address-family + ! + address-family ipv6 unicast + redistribute connected + redistribute kernel + neighbor TOR route-map only-self-out out + neighbor TOR activate + exit-address-family ! bgp as-path access-list SELF permit ^$ ! diff --git a/internal/netconf/testdata/nftrules.v4 b/internal/netconf/testdata/nftrules similarity index 75% rename from internal/netconf/testdata/nftrules.v4 rename to internal/netconf/testdata/nftrules index 5c8ed85..b9291a4 100644 --- a/internal/netconf/testdata/nftrules.v4 +++ b/internal/netconf/testdata/nftrules @@ -1,15 +1,21 @@ # This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . # Do not edit. -table ip metal { +table inet metal { chain input { type filter hook input priority 0; policy drop; - ct state established,related counter accept comment "stateful input" + meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" iifname "lo" counter accept comment "BGP unnumbered" + iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" + iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + tcp dport ssh ct state new counter accept comment "SSH incoming connections" ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" counter jump refuse } @@ -20,17 +26,22 @@ table ip metal { } chain output { type filter hook output priority 0; policy accept; + meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" oifname "lo" counter accept comment "lo output required e.g. for chrony" - ct state established,related counter accept comment "stateful output" + oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" + oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" - ct state invalid counter drop comment "drop invalid packets" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" } chain refuse { limit rate 2/minute counter log prefix "nftables-metal-dropped: " counter drop } } -table ip nat { +table inet nat { chain prerouting { type nat hook prerouting priority 0; policy accept; } diff --git a/internal/netconf/testdata/nftrules_dmz b/internal/netconf/testdata/nftrules_dmz new file mode 100644 index 0000000..4ffa1ce --- /dev/null +++ b/internal/netconf/testdata/nftrules_dmz @@ -0,0 +1,59 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +table inet metal { + chain input { + type filter hook input priority 0; policy drop; + meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" + iifname "lo" counter accept comment "BGP unnumbered" + iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" + iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" + iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" + iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + + tcp dport ssh ct state new counter accept comment "SSH incoming connections" + ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" + ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" + counter jump refuse + } + chain forward { + type filter hook forward priority 0; policy accept; + ct state invalid counter drop comment "drop invalid packets from forwarding to prevent malicious activity" + tcp dport bgp ct state new counter jump refuse comment "block bgp forward to machines" + } + chain output { + type filter hook output priority 0; policy accept; + meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" + oifname "lo" counter accept comment "lo output required e.g. for chrony" + oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" + oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" + } + chain refuse { + limit rate 2/minute counter log prefix "nftables-metal-dropped: " + counter drop + } +} +table inet nat { + chain prerouting { + type nat hook prerouting priority 0; policy accept; + } + chain input { + type nat hook input priority 0; policy accept; + } + chain output { + type nat hook output priority 0; policy accept; + } + chain postrouting { + type nat hook postrouting priority 0; policy accept; + oifname "vlan104009" ip saddr 10.0.16.0/22 counter masquerade comment "snat (networkid: internet-vagrant-lab)" + oifname "vlan104009" ip saddr 10.0.20.0/22 counter masquerade comment "snat (networkid: internet-vagrant-lab)" + } +} \ No newline at end of file diff --git a/internal/netconf/testdata/nftrules.v6 b/internal/netconf/testdata/nftrules_dmz_app similarity index 52% rename from internal/netconf/testdata/nftrules.v6 rename to internal/netconf/testdata/nftrules_dmz_app index de29875..191923a 100644 --- a/internal/netconf/testdata/nftrules.v6 +++ b/internal/netconf/testdata/nftrules_dmz_app @@ -1,31 +1,57 @@ # This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . # Do not edit. -table ip6 metal { +table inet metal { chain input { type filter hook input priority 0; policy drop; meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" iifname "lo" counter accept comment "BGP unnumbered" - ct state established,related counter accept comment "stateful input" iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" + iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" + iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + + tcp dport ssh ct state new counter accept comment "SSH incoming connections" + ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" + ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" counter jump refuse } chain forward { - type filter hook forward priority 0; policy drop; + type filter hook forward priority 0; policy accept; ct state invalid counter drop comment "drop invalid packets from forwarding to prevent malicious activity" - counter jump refuse + tcp dport bgp ct state new counter jump refuse comment "block bgp forward to machines" } chain output { - type filter hook output priority 0; policy drop; - ct state established,related counter accept comment "stateful output" - oifname "lo" counter accept comment "BGP unnumbered" + type filter hook output priority 0; policy accept; meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" + oifname "lo" counter accept comment "lo output required e.g. for chrony" oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" } chain refuse { limit rate 2/minute counter log prefix "nftables-metal-dropped: " counter drop } +} +table inet nat { + chain prerouting { + type nat hook prerouting priority 0; policy accept; + } + chain input { + type nat hook input priority 0; policy accept; + } + chain output { + type nat hook output priority 0; policy accept; + } + chain postrouting { + type nat hook postrouting priority 0; policy accept; + } } \ No newline at end of file diff --git a/internal/netconf/testdata/nftrules_ipv6 b/internal/netconf/testdata/nftrules_ipv6 new file mode 100644 index 0000000..1945551 --- /dev/null +++ b/internal/netconf/testdata/nftrules_ipv6 @@ -0,0 +1,59 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +table inet metal { + chain input { + type filter hook input priority 0; policy drop; + meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" + iifname "lo" counter accept comment "BGP unnumbered" + iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" + iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" + iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" + iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + + tcp dport ssh ct state new counter accept comment "SSH incoming connections" + ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" + ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" + counter jump refuse + } + chain forward { + type filter hook forward priority 0; policy accept; + ct state invalid counter drop comment "drop invalid packets from forwarding to prevent malicious activity" + tcp dport bgp ct state new counter jump refuse comment "block bgp forward to machines" + } + chain output { + type filter hook output priority 0; policy accept; + meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" + oifname "lo" counter accept comment "lo output required e.g. for chrony" + oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" + oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" + } + chain refuse { + limit rate 2/minute counter log prefix "nftables-metal-dropped: " + counter drop + } +} +table inet nat { + chain prerouting { + type nat hook prerouting priority 0; policy accept; + } + chain input { + type nat hook input priority 0; policy accept; + } + chain output { + type nat hook output priority 0; policy accept; + } + chain postrouting { + type nat hook postrouting priority 0; policy accept; + oifname "vlan104009" ip6 saddr 2002::/64 counter masquerade comment "snat (networkid: internet-vagrant-lab)" + oifname "vlan104010" ip6 saddr 2002::/64 counter masquerade comment "snat (networkid: mpls-nbg-w8101-test)" + } +} \ No newline at end of file diff --git a/internal/netconf/testdata/nftrules_shared b/internal/netconf/testdata/nftrules_shared new file mode 100644 index 0000000..1ac7f37 --- /dev/null +++ b/internal/netconf/testdata/nftrules_shared @@ -0,0 +1,59 @@ +# This file was auto generated for machine: 'e0ab02d2-27cd-5a5e-8efc-080ba80cf258' by app version . +# Do not edit. +table inet metal { + chain input { + type filter hook input priority 0; policy drop; + meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" + iifname "lo" counter accept comment "BGP unnumbered" + iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" + iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" + iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" + iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + + tcp dport ssh ct state new counter accept comment "SSH incoming connections" + ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" + ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" + counter jump refuse + } + chain forward { + type filter hook forward priority 0; policy accept; + ct state invalid counter drop comment "drop invalid packets from forwarding to prevent malicious activity" + tcp dport bgp ct state new counter jump refuse comment "block bgp forward to machines" + } + chain output { + type filter hook output priority 0; policy accept; + meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" + oifname "lo" counter accept comment "lo output required e.g. for chrony" + oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" + oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" + } + chain refuse { + limit rate 2/minute counter log prefix "nftables-metal-dropped: " + counter drop + } +} +table inet nat { + chain prerouting { + type nat hook prerouting priority 0; policy accept; + } + chain input { + type nat hook input priority 0; policy accept; + } + chain output { + type nat hook output priority 0; policy accept; + } + chain postrouting { + type nat hook postrouting priority 0; policy accept; + oifname "vlan3982" ip saddr 10.0.18.0/22 counter masquerade comment "snat (networkid: storage-net)" + oifname "vlan104009" ip saddr 10.0.18.0/22 counter masquerade comment "snat (networkid: internet-vagrant-lab)" + } +} \ No newline at end of file diff --git a/internal/netconf/tpl/frr.firewall.tpl b/internal/netconf/tpl/frr.firewall.tpl index 86f41c5..8e8f918 100644 --- a/internal/netconf/tpl/frr.firewall.tpl +++ b/internal/netconf/tpl/frr.firewall.tpl @@ -42,6 +42,12 @@ router bgp {{ .ASN }} neighbor FABRIC route-map only-self-out out exit-address-family ! + address-family ipv6 unicast + redistribute connected route-map LOOPBACKS + neighbor FABRIC route-map only-self-out out + neighbor FABRIC activate + exit-address-family + ! address-family l2vpn evpn neighbor FABRIC activate advertise-all-vni @@ -60,14 +66,23 @@ router bgp {{ $ASN }} vrf vrf{{ .ID }} import vrf route-map vrf{{ .ID }}-import-map exit-address-family ! + address-family ipv6 unicast + redistribute connected + {{- range .ImportVRFNames }} + import vrf {{ . }} + {{- end }} + import vrf route-map vrf{{ .ID }}-import-map + exit-address-family + ! address-family l2vpn evpn advertise ipv4 unicast + advertise ipv6 unicast exit-address-family ! {{- end }} {{- range .VRFs }} {{- range .IPPrefixLists }} -ip prefix-list {{ .Name }} {{ .Spec }} +{{ .AddressFamily }} prefix-list {{ .Name }} {{ .Spec }} {{- end}} {{- range .RouteMaps }} route-map {{ .Name }} {{ .Policy }} {{ .Order }} diff --git a/internal/netconf/tpl/frr.machine.tpl b/internal/netconf/tpl/frr.machine.tpl index b9510ea..73375a5 100644 --- a/internal/netconf/tpl/frr.machine.tpl +++ b/internal/netconf/tpl/frr.machine.tpl @@ -44,6 +44,13 @@ router bgp {{ .ASN }} redistribute kernel neighbor TOR route-map only-self-out out exit-address-family + ! + address-family ipv6 unicast + redistribute connected + redistribute kernel + neighbor TOR route-map only-self-out out + neighbor TOR activate + exit-address-family ! bgp as-path access-list SELF permit ^$ ! diff --git a/internal/netconf/tpl/networkd/00-lo.network.tpl b/internal/netconf/tpl/networkd/00-lo.network.tpl index f529d9f..f522931 100644 --- a/internal/netconf/tpl/networkd/00-lo.network.tpl +++ b/internal/netconf/tpl/networkd/00-lo.network.tpl @@ -8,5 +8,5 @@ Address=127.0.0.1/8 {{- range .Loopback.IPs }} [Address] -Address={{ . }}/32 +Address={{ . }} {{- end }} \ No newline at end of file diff --git a/internal/netconf/tpl/networkd/30-svi.network.tpl b/internal/netconf/tpl/networkd/30-svi.network.tpl index 1e37844..7b313f0 100644 --- a/internal/netconf/tpl/networkd/30-svi.network.tpl +++ b/internal/netconf/tpl/networkd/30-svi.network.tpl @@ -9,5 +9,5 @@ MTUBytes=9000 [Network] VRF=vrf{{ .VRF.ID }} {{- range .SVI.Addresses }} -Address={{ . }}/32 +Address={{ . }} {{- end }} diff --git a/internal/netconf/tpl/rules.v4.tpl b/internal/netconf/tpl/nftrules.tpl similarity index 72% rename from internal/netconf/tpl/rules.v4.tpl rename to internal/netconf/tpl/nftrules.tpl index ada2321..7c75fe0 100644 --- a/internal/netconf/tpl/rules.v4.tpl +++ b/internal/netconf/tpl/nftrules.tpl @@ -1,15 +1,21 @@ {{- /*gotype: github.com/metal-stack/metal-networker/internal/netconf.IptablesData*/ -}} {{ .Comment }} -table ip metal { +table inet metal { chain input { type filter hook input priority 0; policy drop; - ct state established,related counter accept comment "stateful input" + meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" iifname "lo" counter accept comment "BGP unnumbered" + iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" + iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" iifname "lan0" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan0" iifname "lan1" ip saddr 10.0.0.0/8 udp dport 4789 counter accept comment "incoming VXLAN lan1" + + ct state established,related counter accept comment "stateful input" + tcp dport ssh ct state new counter accept comment "SSH incoming connections" ip saddr 10.0.0.0/8 tcp dport 9100 counter accept comment "node metrics" ip saddr 10.0.0.0/8 tcp dport 9630 counter accept comment "nftables metrics" + ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" counter jump refuse } @@ -20,17 +26,22 @@ table ip metal { } chain output { type filter hook output priority 0; policy accept; + meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" oifname "lo" counter accept comment "lo output required e.g. for chrony" - ct state established,related counter accept comment "stateful output" + oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" + oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" + ip daddr 10.0.0.0/8 udp dport 4789 counter accept comment "outgoing VXLAN" - ct state invalid counter drop comment "drop invalid packets" + + ct state established,related counter accept comment "stateful output" + ct state invalid counter drop comment "drop invalid packets" } chain refuse { limit rate 2/minute counter log prefix "nftables-metal-dropped: " counter drop } } -table ip nat { +table inet nat { chain prerouting { type nat hook prerouting priority 0; policy accept; } @@ -46,7 +57,7 @@ table ip nat { {{- $cmt:=.Comment }} {{- $out:=.OutInterface }} {{- range .SourceSpecs }} - oifname "{{ $out }}" ip saddr {{ . }} counter masquerade comment "{{ $cmt }}" + oifname "{{ $out }}" {{ .AddressFamily }} saddr {{ .Source }} counter masquerade comment "{{ $cmt }}" {{- end }} {{- end }} } diff --git a/internal/netconf/tpl/rules.v6.tpl b/internal/netconf/tpl/rules.v6.tpl deleted file mode 100644 index 45946f0..0000000 --- a/internal/netconf/tpl/rules.v6.tpl +++ /dev/null @@ -1,31 +0,0 @@ -{{- /*gotype: github.com/metal-stack/metal-networker/internal/netconf.IptablesData*/ -}} -{{ .Comment }} -table ip6 metal { - chain input { - type filter hook input priority 0; policy drop; - meta l4proto ipv6-icmp counter accept comment "icmpv6 input required for neighbor discovery" - iifname "lo" counter accept comment "BGP unnumbered" - ct state established,related counter accept comment "stateful input" - iifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan0" - iifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered input from lan1" - ct state invalid counter drop comment "drop invalid packets to prevent malicious activity" - counter jump refuse - } - chain forward { - type filter hook forward priority 0; policy drop; - ct state invalid counter drop comment "drop invalid packets from forwarding to prevent malicious activity" - counter jump refuse - } - chain output { - type filter hook output priority 0; policy drop; - ct state established,related counter accept comment "stateful output" - oifname "lo" counter accept comment "BGP unnumbered" - meta l4proto ipv6-icmp counter accept comment "icmpv6 output required for neighbor discovery" - oifname "lan0" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan0" - oifname "lan1" ip6 saddr fe80::/64 tcp dport bgp counter accept comment "bgp unnumbered output at lan1" - } - chain refuse { - limit rate 2/minute counter log prefix "nftables-metal-dropped: " - counter drop - } -} \ No newline at end of file diff --git a/pkg/exec/verbosecmd.go b/pkg/exec/verbosecmd.go index 6ba70b4..662eda4 100644 --- a/pkg/exec/verbosecmd.go +++ b/pkg/exec/verbosecmd.go @@ -25,7 +25,7 @@ func (v VerboseCmd) Run() error { err := v.Cmd.Run() if err != nil { - return fmt.Errorf("%v: %s", err, stderr.String()) + return fmt.Errorf("%w: %s", err, stderr.String()) } return nil diff --git a/pkg/net/reloader.go b/pkg/net/reloader.go index 202bc28..ceacd26 100644 --- a/pkg/net/reloader.go +++ b/pkg/net/reloader.go @@ -27,7 +27,7 @@ type DBusReloader struct { func (r DBusReloader) Reload() error { dbc, err := dbus.New() if err != nil { - return fmt.Errorf("unable to connect to dbus: %v", err) + return fmt.Errorf("unable to connect to dbus: %w", err) } defer dbc.Close() @@ -55,7 +55,7 @@ type DBusStartReloader struct { func (r DBusStartReloader) Reload() error { dbc, err := dbus.New() if err != nil { - return fmt.Errorf("unable to connect to dbus: %v", err) + return fmt.Errorf("unable to connect to dbus: %w", err) } defer dbc.Close() diff --git a/pkg/net/validator.go b/pkg/net/validator.go index bebfaae..1d24430 100644 --- a/pkg/net/validator.go +++ b/pkg/net/validator.go @@ -21,7 +21,7 @@ type DBusTemplateValidator struct { func (v DBusTemplateValidator) Validate() error { dbc, err := dbus.New() if err != nil { - return fmt.Errorf("unable to connect to dbus: %v", err) + return fmt.Errorf("unable to connect to dbus: %w", err) } defer dbc.Close()