github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/internal/operator/orbiter/kinds/loadbalancers/dynamic/adapt.go (about) 1 package dynamic 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strconv" 8 "strings" 9 "text/template" 10 11 "github.com/caos/orbos/internal/helpers" 12 "github.com/caos/orbos/internal/operator/common" 13 "github.com/caos/orbos/internal/operator/nodeagent/dep/sysctl" 14 "github.com/caos/orbos/internal/operator/orbiter" 15 "github.com/caos/orbos/internal/operator/orbiter/kinds/clusters/core/infra" 16 "github.com/caos/orbos/internal/operator/orbiter/kinds/providers/core" 17 "github.com/caos/orbos/mntr" 18 "github.com/caos/orbos/pkg/secret" 19 "github.com/caos/orbos/pkg/tree" 20 21 "github.com/prometheus/client_golang/prometheus" 22 ) 23 24 const ( 25 nginxVersion = "v1.18.0" 26 keepalivedVersion = "v1.3.5" 27 ) 28 29 var probes = prometheus.NewGaugeVec( 30 prometheus.GaugeOpts{ 31 Name: "probe", 32 Help: "Load Balancing Probes.", 33 }, 34 []string{"name", "type", "target"}, 35 ) 36 37 func init() { 38 prometheus.MustRegister(probes) 39 } 40 41 type WhiteListFunc func() []*orbiter.CIDR 42 43 type VRRP struct { 44 VRRPInterface string 45 VIPInterface string 46 NotifyMaster func(machine infra.Machine) (string, bool) 47 AuthCheck func(machine infra.Machine) (string, int) 48 } 49 50 func AdaptFunc(whitelist WhiteListFunc) orbiter.AdaptFunc { 51 return func(monitor mntr.Monitor, finishedChan chan struct{}, desiredTree *tree.Tree, currentTree *tree.Tree) (queryFunc orbiter.QueryFunc, destroyFunc orbiter.DestroyFunc, configureFunc orbiter.ConfigureFunc, migrate bool, secrets map[string]*secret.Secret, err error) { 52 53 defer func() { 54 if err != nil { 55 err = fmt.Errorf("building %s failed: %w", desiredTree.Common.Kind, err) 56 } 57 }() 58 if desiredTree.Common.Version() != "v2" { 59 migrate = true 60 } 61 desiredKind := &Desired{Common: desiredTree.Common} 62 if err := desiredTree.Original.Decode(desiredKind); err != nil { 63 return nil, nil, nil, migrate, nil, fmt.Errorf("unmarshaling desired state for kind %s failed: %w", desiredTree.Common.Kind, err) 64 } 65 66 for _, pool := range desiredKind.Spec { 67 for _, vip := range pool { 68 for _, t := range vip.Transport { 69 sort.Strings(t.BackendPools) 70 if t.ProxyProtocol == nil { 71 trueVal := true 72 t.ProxyProtocol = &trueVal 73 migrate = true 74 } 75 if t.Name == "kubeapi" { 76 if t.HealthChecks.Path != "/healthz" { 77 t.HealthChecks.Path = "/healthz" 78 migrate = true 79 } 80 if t.HealthChecks.Protocol != "https" { 81 t.HealthChecks.Protocol = "https" 82 migrate = true 83 } 84 if t.ProxyProtocol == nil || *t.ProxyProtocol { 85 f := false 86 t.ProxyProtocol = &f 87 migrate = true 88 } 89 } 90 if len(t.Whitelist) == 0 { 91 allIPs := orbiter.CIDR("0.0.0.0/0") 92 t.Whitelist = []*orbiter.CIDR{&allIPs} 93 migrate = true 94 } 95 } 96 } 97 } 98 99 if err := desiredKind.Validate(); err != nil { 100 return nil, nil, nil, migrate, nil, err 101 } 102 desiredTree.Parsed = desiredKind 103 104 current := &Current{ 105 Common: tree.NewCommon("orbiter.caos.ch/DynamicLoadBalancer", "v0", false), 106 } 107 currentTree.Parsed = current 108 109 return func(nodeAgentsCurrent *common.CurrentNodeAgents, nodeagents *common.DesiredNodeAgents, queried map[string]interface{}) (orbiter.EnsureFunc, error) { 110 111 wl := whitelist() 112 113 addresses := make(map[string]*infra.Address) 114 for _, pool := range desiredKind.Spec { 115 for _, vip := range pool { 116 for _, t := range vip.Transport { 117 addresses[t.Name] = &infra.Address{ 118 Location: vip.IP, 119 FrontendPort: uint16(t.FrontendPort), 120 BackendPort: uint16(t.BackendPort), 121 } 122 } 123 } 124 } 125 126 poolMachines := curryPoolMachines() 127 enrichedVIPs := curryEnrichedVIPs(*desiredKind, poolMachines, wl, nodeAgentsCurrent) 128 129 current.Current.Spec = enrichedVIPs 130 current.Current.Desire = func(forPool string, svc core.MachinesService, vrrp *VRRP, mapVIP func(*VIP) string) (bool, error) { 131 var lbMachines infra.Machines 132 133 done := true 134 desireNodeAgent := func(machine infra.Machine, fw common.Firewall, nginx, keepalived common.Package) { 135 machineMonitor := monitor.WithField("machine", machine.ID()) 136 deepNa, _ := nodeagents.Get(machine.ID()) 137 deepNaCurr, _ := nodeAgentsCurrent.Get(machine.ID()) 138 139 if !deepNa.Firewall.Contains(fw) { 140 machineMonitor.WithField("open", fw.ToCurrent()).Debug("Loadbalancing firewall desired") 141 } 142 deepNa.Firewall.Merge(fw) 143 if !fw.IsContainedIn(deepNaCurr.Open) { 144 machineMonitor.WithField("ports", deepNa.Firewall.ToCurrent()).Info("Awaiting firewalld config") 145 done = false 146 } 147 for _, port := range fw.Ports("external") { 148 if portInt, parseErr := strconv.ParseInt(port.Port, 10, 16); parseErr == nil && portInt == 22 { 149 150 if deepNa.Software.SSHD.Config == nil || deepNa.Software.SSHD.Config["listenaddress"] != machine.IP() { 151 deepNa.Software.SSHD.Config = map[string]string{"listenaddress": machine.IP()} 152 machineMonitor.Changed("sshd config desired") 153 } 154 155 if !deepNaCurr.Software.SSHD.Equals(deepNa.Software.SSHD) { 156 machineMonitor.Info("Awaiting sshd config") 157 done = false 158 } 159 } 160 } 161 if deepNa.Software == nil { 162 deepNa.Software = &common.Software{} 163 } 164 165 if !nginx.Equals(common.Package{}) { 166 if !deepNa.Software.Nginx.Equals(nginx) { 167 machineMonitor.WithField("pkg", nginx).Debug("NGINX desired") 168 } 169 deepNa.Software.Nginx = nginx 170 if !deepNa.Software.Nginx.Equals(deepNaCurr.Software.Nginx) { 171 machineMonitor.Info("Awaiting NGINX") 172 done = false 173 } 174 if !sysctl.Contains(deepNa.Software.Sysctl, common.Package{ 175 Config: map[string]string{ 176 string(common.IpForward): "1", 177 string(common.NonLocalBind): "1", 178 }, 179 }) { 180 machineMonitor.Changed("sysctl desired") 181 } 182 sysctl.Enable(&deepNa.Software.Sysctl, common.IpForward) 183 sysctl.Enable(&deepNa.Software.Sysctl, common.NonLocalBind) 184 if !sysctl.Contains(deepNaCurr.Software.Sysctl, deepNa.Software.Sysctl) { 185 machineMonitor.Info("Awaiting sysctl config") 186 done = false 187 } 188 } 189 190 if !keepalived.Equals(common.Package{}) { 191 if !deepNa.Software.KeepaliveD.Equals(keepalived) { 192 machineMonitor.WithField("pkg", keepalived).Debug("Keepalived desired") 193 } 194 deepNa.Software.KeepaliveD = keepalived 195 if !deepNa.Software.KeepaliveD.Equals(deepNaCurr.Software.KeepaliveD) { 196 monitor.Info("Awaiting keepalived") 197 done = false 198 } 199 } 200 } 201 202 templateFuncs := template.FuncMap(map[string]interface{}{ 203 "forMachines": func(poolName string) (infra.Machines, error) { 204 machines, err := svc.List(poolName) 205 if err != nil { 206 return nil, err 207 } 208 sort.Sort(machines) 209 return machines, nil 210 }, 211 "add": func(i, y int) int { return i + y }, 212 "user": func(machine infra.Machine) (string, error) { 213 var user string 214 whoami := "whoami" 215 stdout, err := machine.Execute(nil, whoami) 216 if err != nil { 217 return "", fmt.Errorf("running command %s remotely failed", whoami) 218 } 219 user = strings.TrimSuffix(string(stdout), "\n") 220 monitor.WithFields(map[string]interface{}{ 221 "user": user, 222 "machine": machine.ID(), 223 "command": whoami, 224 }).Debug("Executed command") 225 return user, nil 226 }, 227 "vip": mapVIP, 228 "routerID": func(vip *VIP) string { 229 vipParts := strings.Split(mapVIP(vip), ".") 230 if len(vipParts) != 4 || vipParts[3] == "0" { 231 return "55" 232 } 233 return vipParts[3] 234 }, 235 "derefBool": func(in *bool) bool { return in != nil && *in }, 236 }) 237 238 var nginxNATTemplate *template.Template 239 var vips []*VIP 240 241 if vrrp == nil { 242 for _, desiredVIPs := range desiredKind.Spec { 243 vips = append(vips, desiredVIPs...) 244 } 245 nginxNATTemplate = template.Must(template.New("").Funcs(templateFuncs).Parse(`worker_rlimit_nofile 8192; 246 247 events { 248 worker_connections 4096; ## Default: 1024 249 } 250 stream { {{ range $nat := .NATs }} 251 upstream {{ $nat.Name }} { 252 server {{ $nat.To }}; 253 } 254 255 {{ range $from := $nat.From }} server { 256 listen {{ $from }}; 257 258 {{ range $white := $nat.Whitelist }} allow {{ $white }}; 259 {{ end }} 260 deny all; 261 proxy_pass {{ $nat.Name }}; 262 proxy_protocol {{ if derefBool $nat.ProxyProtocol }}on{{ else }}off{{ end }}; 263 } 264 {{ end }}{{ end }}}`)) 265 266 } else { 267 268 lbMachines = nil 269 if err := poolMachines(svc, func(pool string, machines infra.Machines) { 270 if forPool == pool { 271 lbMachines = machines 272 } 273 }); err != nil { 274 return false, err 275 } 276 277 sort.Sort(lbMachines) 278 279 spec, _, err := enrichedVIPs(svc) 280 if err != nil { 281 return false, err 282 } 283 284 lbData := make([]LB, len(lbMachines)) 285 for idx, machine := range lbMachines { 286 lbData[idx] = LB{ 287 VIPs: spec[forPool], 288 Self: machine, 289 Peers: deriveFilterMachines(func(cmp infra.Machine) bool { 290 return cmp.ID() != machine.ID() 291 }, append([]infra.Machine(nil), lbMachines...)), 292 State: "BACKUP", 293 CustomMasterNotifyer: vrrp.NotifyMaster != nil, 294 VRRPInterface: vrrp.VRRPInterface, 295 VIPInterface: vrrp.VIPInterface, 296 } 297 if idx == 0 { 298 lbData[idx].State = "MASTER" 299 } 300 } 301 302 keepaliveDTemplate := template.Must(template.New("").Funcs(templateFuncs).Parse(`{{ $root := . }}global_defs { 303 enable_script_security 304 script_user {{ user $root.Self }} 305 } 306 307 vrrp_sync_group VG1 { 308 group { 309 {{ range $idx, $_ := .VIPs }} VI_{{ $idx }} 310 {{ end }} } 311 } 312 313 {{ range $idx, $vip := .VIPs }}vrrp_script chk_{{ vip $vip }} { 314 script "/usr/local/bin/health --protocol http --ip 127.0.0.1 --port 29999 --path /ready --status 200" 315 interval 2 # check every 2 seconds 316 fall 5 # require 5 failures for KO 317 rise 5 # require 5 successes for OK 318 timeout 5 # time out after 5 seconds 319 } 320 321 vrrp_instance VI_{{ $idx }} { 322 state {{ $root.State }} 323 unicast_src_ip {{ $root.Self.IP }} 324 unicast_peer { 325 {{ range $peer := $root.Peers }}{{ $peer.IP }} 326 {{ end }} } 327 interface {{ $root.VRRPInterface }} 328 virtual_router_id {{ routerID $vip }} 329 advert_int 1 330 authentication { 331 auth_type PASS 332 auth_pass [ REDACTED ] 333 } 334 track_script { 335 chk_{{ vip $vip }} 336 } 337 338 virtual_ipaddress { 339 {{ vip $vip }} dev {{ $root.VIPInterface }} 340 } 341 342 {{ if $root.CustomMasterNotifyer }} notify_master "/etc/keepalived/notifymaster.sh" 343 {{ else }} virtual_ipaddress { 344 {{ vip $vip }} 345 } 346 {{ end }} 347 } 348 {{ end }}`)) 349 350 nginxLBTemplate := template.Must(template.New("").Funcs(templateFuncs).Parse(`{{ $root := . }}worker_rlimit_nofile 8192; 351 352 events { 353 worker_connections 4096; ## Default: 1024 354 } 355 356 stream { {{ range $vip := .VIPs }}{{ range $src := $vip.Transport }} 357 upstream {{ $src.Name }} { {{ range $dest := $src.BackendPools }}{{ range $machine := forMachines $dest }} 358 server {{ $machine.IP }}:{{ $src.BackendPort }}; # {{ $dest }}{{end}}{{ end }} 359 } 360 server { 361 listen {{ vip $vip }}:{{ $src.FrontendPort }}; 362 {{ range $white := $src.Whitelist }} allow {{ $white }}; 363 {{ end }} 364 deny all; 365 proxy_pass {{ $src.Name }}; 366 proxy_protocol {{ if derefBool $src.ProxyProtocol }}on{{ else }}off{{ end }}; 367 } 368 {{ end }}{{ end }}} 369 370 http { 371 server { 372 listen 29999; 373 374 location /ready { 375 return 200; 376 } 377 } 378 }`)) 379 380 for _, d := range lbData { 381 382 if len(d.VIPs) == 0 { 383 continue 384 } 385 386 ngxBuf := new(bytes.Buffer) 387 //noinspection GoDeferInLoop 388 defer ngxBuf.Reset() 389 kaBuf := new(bytes.Buffer) 390 defer kaBuf.Reset() 391 392 if err := keepaliveDTemplate.Execute(kaBuf, d); err != nil { 393 return false, err 394 } 395 396 kaPkg := common.Package{Version: keepalivedVersion, Config: map[string]string{"keepalived.conf": kaBuf.String()}} 397 kaBuf.Reset() 398 399 if d.CustomMasterNotifyer { 400 var enforceEnsuring bool 401 kaPkg.Config["notifymaster.sh"], enforceEnsuring = vrrp.NotifyMaster(d.Self) 402 if enforceEnsuring { 403 kaPkg.Config["reensure"] = "true" 404 } 405 } 406 407 if vrrp.AuthCheck != nil { 408 authCheck, expectedExitCode := vrrp.AuthCheck(d.Self) 409 if authCheck != "" { 410 kaPkg.Config["authcheck.sh"] = authCheck 411 kaPkg.Config["authcheckexitcode"] = strconv.Itoa(expectedExitCode) 412 } 413 } 414 415 if err := nginxLBTemplate.Execute(ngxBuf, d); err != nil { 416 return false, err 417 } 418 ngxPkg := desireNginx(ngxBuf.String()) 419 ngxBuf.Reset() 420 421 desireNodeAgent(d.Self, common.ToFirewall("external", make(map[string]*common.Allowed)), ngxPkg, kaPkg) 422 } 423 } 424 425 nodesNats := make(map[string]*NATDesires) 426 spec, _, err := enrichedVIPs(svc) 427 if err != nil { 428 return false, err 429 } 430 for srcPool, vips := range spec { 431 for _, vip := range vips { 432 for _, transport := range vip.Transport { 433 srcFW := map[string]*common.Allowed{ 434 fmt.Sprintf("%s-%d-src", transport.Name, transport.FrontendPort): { 435 Port: fmt.Sprintf("%d", transport.FrontendPort), 436 Protocol: "tcp", 437 }, 438 } 439 ip := mapVIP(vip) 440 var vipProbed bool 441 probeVIP := func() { 442 if vipProbed { 443 return 444 } 445 probe("VIP", ip, uint16(transport.FrontendPort), false, transport.HealthChecks, *transport) 446 vipProbed = true 447 } 448 449 if vrrp != nil && forPool == srcPool { 450 for _, machine := range lbMachines { 451 desireNodeAgent(machine, common.ToFirewall("external", srcFW), common.Package{}, common.Package{}) 452 } 453 probeVIP() 454 } 455 for _, dest := range transport.BackendPools { 456 457 destFW := map[string]*common.Allowed{ 458 fmt.Sprintf("%s-%d-dest", transport.Name, transport.BackendPort): { 459 Port: fmt.Sprintf("%d", transport.BackendPort), 460 Protocol: "tcp", 461 }, 462 } 463 464 destMachines, err := svc.List(dest) 465 if err != nil { 466 return false, err 467 } 468 469 for idx := range destMachines { 470 machine := destMachines[idx] 471 desireNodeAgent(machine, common.ToFirewall("internal", destFW), common.Package{}, common.Package{}) 472 probe("Upstream", machine.IP(), uint16(transport.BackendPort), *transport.ProxyProtocol, transport.HealthChecks, *transport) 473 if vrrp != nil || forPool != dest { 474 continue 475 } 476 probeVIP() 477 478 nodeNatDesires, ok := nodesNats[machine.IP()] 479 if !ok { 480 nodeNatDesires = &NATDesires{NATs: make([]*NAT, 0)} 481 } 482 nodeNatDesires.Firewall.Merge(common.ToFirewall("external", srcFW)) 483 nodeNatDesires.Machine = machine 484 485 nodeNatDesires.NATs = append(nodeNatDesires.NATs, &NAT{ 486 Whitelist: transport.Whitelist, 487 Name: transport.Name, 488 From: []string{ 489 fmt.Sprintf("%s:%d", ip, transport.FrontendPort), // VIP 490 fmt.Sprintf("%s:%d", machine.IP(), transport.FrontendPort), // Node IP 491 }, 492 To: fmt.Sprintf("%s:%d", machine.IP(), transport.BackendPort), 493 ProxyProtocol: *transport.ProxyProtocol, 494 }) 495 nodesNats[machine.IP()] = nodeNatDesires 496 } 497 } 498 } 499 } 500 } 501 502 for _, node := range nodesNats { 503 ngxBuf := new(bytes.Buffer) 504 //noinspection GoDeferInLoop 505 defer ngxBuf.Reset() 506 if err := nginxNATTemplate.Execute(ngxBuf, struct { 507 NATs []*NAT 508 }{ 509 NATs: node.NATs, 510 }); err != nil { 511 return false, err 512 } 513 ngxPkg := desireNginx(ngxBuf.String()) 514 ngxBuf.Reset() 515 desireNodeAgent(node.Machine, node.Firewall, ngxPkg, common.Package{}) 516 } 517 return done, nil 518 } 519 return orbiter.NoopEnsure, nil 520 }, orbiter.NoopDestroy, orbiter.NoopConfigure, migrate, make(map[string]*secret.Secret, 0), nil 521 } 522 } 523 524 func addToWhitelists(makeUnique bool, vips []*VIP, cidr ...*orbiter.CIDR) []*VIP { 525 newVIPs := make([]*VIP, len(vips)) 526 for vipIdx, vip := range vips { 527 newTransport := make([]*Transport, len(vip.Transport)) 528 for srcIdx, src := range vip.Transport { 529 newSource := &Transport{ 530 Name: src.Name, 531 FrontendPort: src.FrontendPort, 532 BackendPort: src.BackendPort, 533 BackendPools: src.BackendPools, 534 Whitelist: append(src.Whitelist, cidr...), 535 HealthChecks: src.HealthChecks, 536 ProxyProtocol: src.ProxyProtocol, 537 } 538 if makeUnique { 539 newSource.Whitelist = unique(newSource.Whitelist) 540 } 541 newTransport[srcIdx] = newSource 542 } 543 newVIPs[vipIdx] = &VIP{ 544 IP: vip.IP, 545 Transport: newTransport, 546 } 547 } 548 return newVIPs 549 } 550 551 func probe(probeType, ip string, port uint16, proxyProtocol bool, hc HealthChecks, source Transport) { 552 553 var success float64 554 _, err := helpers.Check(hc.Protocol, ip, port, hc.Path, int(hc.Code), proxyProtocol) 555 if err == nil { 556 success = 1 557 } 558 559 probes.With(prometheus.Labels{ 560 "name": source.Name, 561 "type": probeType, 562 "target": fmt.Sprintf("%s://%s:%d%s", hc.Protocol, ip, port, hc.Path), 563 }).Set(success) 564 } 565 566 type NATDesires struct { 567 NATs []*NAT 568 Machine infra.Machine 569 Firewall common.Firewall 570 } 571 572 type NAT struct { 573 Name string 574 Whitelist []*orbiter.CIDR 575 From []string 576 To string 577 ProxyProtocol bool 578 } 579 580 type LB struct { 581 VIPs []*VIP 582 State string 583 RouterID int 584 Self infra.Machine 585 Peers []infra.Machine 586 CustomMasterNotifyer bool 587 VRRPInterface string 588 VIPInterface string 589 } 590 591 func unique(s []*orbiter.CIDR) []*orbiter.CIDR { 592 m := make(map[string]bool, len(s)) 593 us := make([]*orbiter.CIDR, len(m)) 594 for _, elem := range s { 595 if len(*elem) != 0 { 596 if !m[string(*elem)] { 597 us = append(us, elem) 598 m[string(*elem)] = true 599 } 600 } 601 } 602 603 cidrs := orbiter.CIDRs(us) 604 sort.Sort(cidrs) 605 return cidrs 606 } 607 608 type poolMachinesFunc func(svc core.MachinesService, do func(string, infra.Machines)) error 609 610 func curryPoolMachines() poolMachinesFunc { 611 var poolsCache map[string]infra.Machines 612 return func(svc core.MachinesService, do func(string, infra.Machines)) error { 613 614 if poolsCache == nil { 615 poolsCache = make(map[string]infra.Machines) 616 allPools, err := svc.ListPools() 617 if err != nil { 618 return err 619 } 620 621 for _, pool := range allPools { 622 machines, err := svc.List(pool) 623 if err != nil { 624 return err 625 } 626 poolsCache[pool] = machines 627 } 628 } 629 630 if poolsCache != nil { 631 for pool, machines := range poolsCache { 632 do(pool, machines) 633 } 634 } 635 return nil 636 } 637 } 638 639 func curryEnrichedVIPs(desired Desired, machines poolMachinesFunc, adaptWhitelist []*orbiter.CIDR, nodeAgents *common.CurrentNodeAgents) func(svc core.MachinesService) (map[string][]*VIP, []AuthCheckResult, error) { 640 var enrichVIPsCache map[string][]*VIP 641 var authCheckResultsCache []AuthCheckResult 642 return func(svc core.MachinesService) (map[string][]*VIP, []AuthCheckResult, error) { 643 if enrichVIPsCache != nil && authCheckResultsCache != nil { 644 return enrichVIPsCache, authCheckResultsCache, nil 645 } 646 enrichVIPsCache = make(map[string][]*VIP) 647 authCheckResultsCache = make([]AuthCheckResult, 0) 648 649 addedCIDRs := append([]*orbiter.CIDR(nil), adaptWhitelist...) 650 if err := machines(svc, func(_ string, machines infra.Machines) { 651 for _, machine := range machines { 652 na, found := nodeAgents.Get(machine.ID()) 653 if found { 654 cfg := na.Software.KeepaliveD.Config 655 if cfg != nil { 656 authCheckExitCode, ok := cfg["authcheckexitcode"] 657 if ok { 658 authCheckExitCodeInt, err := strconv.Atoi(authCheckExitCode) 659 if err == nil { 660 authCheckResultsCache = append(authCheckResultsCache, AuthCheckResult{ 661 Machine: machine, 662 ExitCode: authCheckExitCodeInt, 663 }) 664 } 665 } 666 } 667 } 668 cidr := orbiter.CIDR(fmt.Sprintf("%s/32", machine.IP())) 669 addedCIDRs = append(addedCIDRs, &cidr) 670 } 671 }); err != nil { 672 return nil, nil, err 673 } 674 for deployPool, vips := range desired.Spec { 675 enrichVIPsCache[deployPool] = addToWhitelists(true, vips, addedCIDRs...) 676 } 677 return enrichVIPsCache, authCheckResultsCache, nil 678 } 679 } 680 681 func desireNginx(cfg string) common.Package { 682 return common.Package{ 683 Version: nginxVersion, 684 Config: map[string]string{ 685 "nginx.conf": cfg, 686 "Systemd[Service]LimitNOFILE": "8192", 687 }, 688 } 689 }