Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow import deny for mgmt VRF #79

Merged
merged 6 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions pkg/frr/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,23 @@ type templateConfig struct {
}

func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
config, err := renderSubtemplates(in, nm)
// Remove permit from VRF and only allow deny rules for mgmt VRFs
for i := range in.VRFs {
if in.VRFs[i].Name != m.mgmtVrf {
continue
}
for j := range in.VRFs[i].Import {
for k := range in.VRFs[i].Import[j].Items {
if in.VRFs[i].Import[j].Items[k].Action != "deny" {
return false, fmt.Errorf("only deny rules are allowed in import prefix-lists of mgmt VRFs")
}
// Swap deny to permit, this will be a prefix-list called from a deny route-map
in.VRFs[i].Import[j].Items[k].Action = "permit"
}
}
}

config, err := m.renderSubtemplates(in, nm)
if err != nil {
return false, err
}
Expand All @@ -54,7 +70,15 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
return false, nil
}

func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfig, error) {
func (m *Manager) renderRouteMapMgmtIn() ([]byte, error) {
return render(routeMapMgmtInTpl, mgmtImportConfig{
IPv4MgmtRouteMapIn: m.ipv4MgmtRouteMapIn,
IPv6MgmtRouteMapIn: m.ipv6MgmtRouteMapIn,
MgmtVrfName: m.mgmtVrf,
})
}

func (m *Manager) renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfig, error) {
vrfRouterID, err := nlManager.GetUnderlayIP()
if err != nil {
return nil, fmt.Errorf("error getting underlay IP: %w", err)
Expand Down Expand Up @@ -89,6 +113,10 @@ func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfi
if err != nil {
return nil, err
}
routemapMgmtIn, err := m.renderRouteMapMgmtIn()
if err != nil {
return nil, err
}
asn := in.ASN
if asn == 0 {
asn = vrfAsnConfig
Expand All @@ -110,7 +138,7 @@ func renderSubtemplates(in Configuration, nlManager *nl.Manager) (*templateConfi
NeighborsV6: string(neighborsV6),
BGP: string(bgp),
PrefixLists: string(prefixlists),
RouteMaps: string(routemaps),
RouteMaps: string(routemaps) + "\n" + string(routemapMgmtIn),
UnderlayRouterID: vrfRouterID.String(),
Hostname: hostname,
}, nil
Expand Down
7 changes: 4 additions & 3 deletions pkg/frr/frr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

const (
frrConf = "frr.conf"
mgmtVrf = "mgmt"
)

var (
Expand Down Expand Up @@ -61,20 +62,20 @@ var _ = Describe("frr", func() {
Context("Init() should", func() {
It("return error if cannot read template config", func() {
m := &Manager{}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).To(HaveOccurred())
})
It("return error if cannot write template config file", func() {
m := &Manager{ConfigPath: "testdata/" + frrConf}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).To(HaveOccurred())
})
It("return no error", func() {
m := &Manager{
ConfigPath: tmpDir + "/" + frrConf,
TemplatePath: tmpDir + "/frr.tpl.conf",
}
err := m.Init()
err := m.Init(mgmtVrf)
Expect(err).ToNot(HaveOccurred())
})
})
Expand Down
29 changes: 24 additions & 5 deletions pkg/frr/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ var (

type Manager struct {
configTemplate *template.Template
ConfigPath string
TemplatePath string
Cli *Cli
dbusToolkit dbus.System

ipv4MgmtRouteMapIn *string
ipv6MgmtRouteMapIn *string
mgmtVrf string

ConfigPath string
TemplatePath string
Cli *Cli
dbusToolkit dbus.System
}

type PrefixList struct {
Expand Down Expand Up @@ -69,7 +74,7 @@ func NewFRRManager() *Manager {
}
}

func (m *Manager) Init() error {
func (m *Manager) Init(mgmtVrf string) error {
if _, err := os.Stat(m.TemplatePath); errors.Is(err, os.ErrNotExist) {
err = generateTemplateConfig(m.TemplatePath, m.ConfigPath)
if err != nil {
Expand All @@ -86,6 +91,20 @@ func (m *Manager) Init() error {
return fmt.Errorf("error creating new FRR config: %w", err)
}
m.configTemplate = tpl

m.mgmtVrf = mgmtVrf
routeMap, err := getRouteMapName(m.ConfigPath, "ipv4", m.mgmtVrf)
if err != nil {
return fmt.Errorf("error getting v4 mgmt route-map from FRR config: %w", err)
}
m.ipv4MgmtRouteMapIn = routeMap

routeMap, err = getRouteMapName(m.ConfigPath, "ipv6", m.mgmtVrf)
if err != nil {
return fmt.Errorf("error getting v6 mgmt route-map from FRR config: %w", err)
}
m.ipv6MgmtRouteMapIn = routeMap

return nil
}

Expand Down
40 changes: 33 additions & 7 deletions pkg/frr/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ var vrfRawTpl string
//go:embed tpl/route-map.tpl
var routeMapRawTpl string

// Template for mgmt vrf in route-map
//
//go:embed tpl/route-map-mgmt-in.tpl
var routeMapMgmtInRawTpl string

// Template for ip prefix-list
//
//go:embed tpl/prefix-list.tpl
Expand All @@ -45,13 +50,14 @@ var neighborV6RawTpl string
var bgpInstanceRawTpl string

var (
vrfTpl = mustParse("vrf", vrfRawTpl)
routeMapTpl = mustParse("route-map", routeMapRawTpl)
prefixListTpl = mustParse("prefix-list", prefixListRawTpl)
neighborTpl = mustParse("neighbor", neighborRawTpl)
neighborV4Tpl = mustParse("neighborv4", neighborV4RawTpl)
neighborV6Tpl = mustParse("neighborv6", neighborV6RawTpl)
bgpInstanceTpl = mustParse("bgpinstance", bgpInstanceRawTpl)
vrfTpl = mustParse("vrf", vrfRawTpl)
routeMapTpl = mustParse("route-map", routeMapRawTpl)
routeMapMgmtInTpl = mustParse("route-map-mgmt-in", routeMapMgmtInRawTpl)
prefixListTpl = mustParse("prefix-list", prefixListRawTpl)
neighborTpl = mustParse("neighbor", neighborRawTpl)
neighborV4Tpl = mustParse("neighborv4", neighborV4RawTpl)
neighborV6Tpl = mustParse("neighborv6", neighborV6RawTpl)
bgpInstanceTpl = mustParse("bgpinstance", bgpInstanceRawTpl)
)

type bgpInstanceConfig struct {
Expand All @@ -60,6 +66,12 @@ type bgpInstanceConfig struct {
ASN int
}

type mgmtImportConfig struct {
IPv4MgmtRouteMapIn *string
IPv6MgmtRouteMapIn *string
MgmtVrfName string
}

func mustParse(name, rawtpl string) *template.Template {
tpl, err := template.New(name).Parse(rawtpl)
if err != nil {
Expand All @@ -77,6 +89,20 @@ func render(tpl *template.Template, vrfs interface{}) ([]byte, error) {
return buf.Bytes(), nil
}

func getRouteMapName(file, addressFamily, mgmtVrfName string) (*string, error) {
fileContent, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("error reading frr config file %s: %w", file, err)
}
content := string(fileContent)
re := regexp.MustCompile(`(?ms)address-family\s+` + addressFamily + `\s+unicast.*?neighbor\s+def_` + mgmtVrfName + `\s+route-map (\w*)\s+in`)
matches := re.FindStringSubmatch(content)
if len(matches) != len(re.SubexpNames()) {
return nil, nil
}
return &matches[1], nil
}

func generateTemplateConfig(tplFile, original string) error {
fileContent, err := os.ReadFile(original)
if err != nil {
Expand Down
16 changes: 16 additions & 0 deletions pkg/frr/tpl/route-map-mgmt-in.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{ if .IPv4MgmtRouteMapIn }}
route-map {{ .IPv4MgmtRouteMapIn }} permit 5
call rm_{{ .MgmtVrfName }}_import
on-match next
exit
route-map rm_{{ .MgmtVrfName }}_import permit 65535
exit
{{- end }}
{{ if .IPv6MgmtRouteMapIn }}
route-map {{ .IPv6MgmtRouteMapIn }} permit 5
call rm6_{{ .MgmtVrfName }}_import
on-match next
exit
route-map rm6_{{ .MgmtVrfName }}_import permit 65535
exit
{{- end }}
10 changes: 8 additions & 2 deletions pkg/frr/tpl/route-map.tpl
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{{range $vrf := .}}
{{if not $vrf.IsTaaS}}
{{range $i, $pl := $vrf.Import}}
route-map rm_{{$vrf.Name}}_import permit {{$pl.Seq}}
route-map rm_{{$vrf.Name}}_import {{if $vrf.ShouldTemplateVRF}}permit{{else}}deny{{end}} {{$pl.Seq}}
match ip address prefix-list pl_{{$vrf.Name}}_import_{{$i}}
exit
route-map rm6_{{$vrf.Name}}_import permit {{$pl.Seq}}
route-map rm6_{{$vrf.Name}}_import {{if $vrf.ShouldTemplateVRF}}permit{{else}}deny{{end}} {{$pl.Seq}}
match ipv6 address prefix-list pl_{{$vrf.Name}}_import_{{$i}}
exit
{{- end}}
Expand All @@ -31,5 +31,11 @@ route-map rm6_{{$vrf.Name}}_export permit {{$pl.Seq}}
{{- end}}
exit
{{- end -}}
{{if not $vrf.ShouldTemplateVRF}}
route-map rm_{{$vrf.Name}}_import permit 65535
exit
route-map rm6_{{$vrf.Name}}_import permit 65535
exit
{{- end}}
{{- end -}}
{{- end -}}
14 changes: 7 additions & 7 deletions pkg/reconciler/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,19 @@ func NewReconciler(clusterClient client.Client, anycastTracker *anycast.Tracker,

reconciler.debouncer = debounce.NewDebouncer(reconciler.reconcileDebounced, defaultDebounceTime, logger)

if val := os.Getenv("FRR_CONFIG_FILE"); val != "" {
reconciler.frrManager.ConfigPath = val
}
if err := reconciler.frrManager.Init(); err != nil {
return nil, fmt.Errorf("error trying to init FRR Manager: %w", err)
}

cfg, err := config.LoadConfig()
if err != nil {
return nil, fmt.Errorf("error loading config: %w", err)
}
reconciler.config = cfg

if val := os.Getenv("FRR_CONFIG_FILE"); val != "" {
reconciler.frrManager.ConfigPath = val
}
if err := reconciler.frrManager.Init(cfg.SkipVRFConfig[0]); err != nil {
return nil, fmt.Errorf("error trying to init FRR Manager: %w", err)
}

nc, err := healthcheck.LoadConfig(healthcheck.NetHealthcheckFile)
if err != nil {
return nil, fmt.Errorf("error loading networking healthcheck config: %w", err)
Expand Down