@@ -56,7 +56,10 @@ const (
5656 svcSchedFlagsAnnotation = "kube-router.io/service.schedflags"
5757
5858 LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader"
59- svcIpSetName = "KUBE-SVC-ALL"
59+ localIPsIPSetName = "kube-router-local-ips"
60+ ipvsServicesIPSetName = "kube-router-ipvs-services"
61+ serviceIPsIPSetName = "kube-router-service-ips"
62+ ipvsFirewallChainName = "KUBE-ROUTER-SERVICES"
6063)
6164
6265var (
@@ -214,6 +217,9 @@ type NetworkServicesController struct {
214217 ln LinuxNetworking
215218 readyForUpdates bool
216219
220+ // Map of ipsets that we use.
221+ ipsetMap map [string ]* utils.Set
222+
217223 svcLister cache.Indexer
218224 epLister cache.Indexer
219225 podLister cache.Indexer
@@ -318,6 +324,12 @@ func (nsc *NetworkServicesController) Run(healthChan chan<- *healthcheck.Control
318324 return errors .New (sysctlErr .Error ())
319325 }
320326
327+ // https://github.com/cloudnativelabs/kube-router/issues/282
328+ err = nsc .setupIpvsFirewall ()
329+ if err != nil {
330+ return errors .New ("Error setting up ipvs firewall: " + err .Error ())
331+ }
332+
321333 // loop forever unitl notified to stop on stopCh
322334 for {
323335 select {
@@ -375,52 +387,110 @@ func (nsc *NetworkServicesController) sync() error {
375387 return nil
376388}
377389
390+ func getIpvsFirewallInputChainRule () []string {
391+ // The iptables rule for use in {setup,cleanup}IpvsFirewall.
392+ return []string {
393+ "-m" , "comment" , "--comment" , "handle traffic to IPVS service IPs in custom chain" ,
394+ "-m" , "set" , "--match-set" , serviceIPsIPSetName , "dst" ,
395+ "-j" , ipvsFirewallChainName }
396+ }
397+
378398func (nsc * NetworkServicesController ) setupIpvsFirewall () error {
379- // Add ipset containg all SVCs
399+ /*
400+ - create ipsets
401+ - create firewall rules
402+ */
403+
404+ var err error
405+ var ipset * utils.Set
406+
380407 ipSetHandler , err := utils .NewIPSet (false )
381408 if err != nil {
382409 return err
383410 }
384411
385- svcIpSet , err := ipSetHandler .Create (svcIpSetName , utils .TypeHashIPPort , utils .OptionTimeout , "0" )
412+ // Remember ipsets for use in syncIpvsFirewall
413+ nsc .ipsetMap = make (map [string ]* utils.Set )
414+
415+ // Create ipset for local addresses.
416+ ipset , err = ipSetHandler .Create (localIPsIPSetName , utils .TypeHashIP , utils .OptionTimeout , "0" )
386417 if err != nil {
387418 return fmt .Errorf ("failed to create ipset: %s" , err .Error ())
388419 }
420+ nsc .ipsetMap [localIPsIPSetName ] = ipset
389421
390- ipvsSvcs , err := nsc .ln .ipvsGetServices ()
422+ // Create 2 ipsets for services. One for 'ip' and one for 'ip,port'
423+ ipset , err = ipSetHandler .Create (serviceIPsIPSetName , utils .TypeHashIP , utils .OptionTimeout , "0" )
391424 if err != nil {
392- return errors . New ( "Failed to list IPVS services: " + err .Error ())
425+ return fmt . Errorf ( "failed to create ipset: %s" , err .Error ())
393426 }
427+ nsc .ipsetMap [serviceIPsIPSetName ] = ipset
394428
395- svcSets := make ([]string , 0 , len (ipvsSvcs ))
396- for _ , ipvsSvc := range ipvsSvcs {
397- protocol := "udp"
398- if ipvsSvc .Protocol == syscall .IPPROTO_TCP {
399- protocol = "tcp"
429+ ipset , err = ipSetHandler .Create (ipvsServicesIPSetName , utils .TypeHashIPPort , utils .OptionTimeout , "0" )
430+ if err != nil {
431+ return fmt .Errorf ("failed to create ipset: %s" , err .Error ())
432+ }
433+ nsc .ipsetMap [ipvsServicesIPSetName ] = ipset
434+
435+ // Setup a custom iptables chain to explicitly allow input traffic to
436+ // ipvs services only.
437+ iptablesCmdHandler , err := iptables .New ()
438+ if err != nil {
439+ return errors .New ("Failed to initialize iptables executor" + err .Error ())
440+ }
441+
442+ // ClearChain either clears an existing chain or creates a new one.
443+ err = iptablesCmdHandler .ClearChain ("filter" , ipvsFirewallChainName )
444+ if err != nil {
445+ return fmt .Errorf ("Failed to run iptables command: %s" , err .Error ())
446+ }
447+
448+ var comment string
449+ var args []string
450+
451+ comment = "allow input traffic to ipvs services"
452+ args = []string {"-m" , "comment" , "--comment" , comment ,
453+ "-m" , "set" , "--match-set" , ipvsServicesIPSetName , "dst,dst" ,
454+ "-j" , "ACCEPT" }
455+ exists , err := iptablesCmdHandler .Exists ("filter" , ipvsFirewallChainName , args ... )
456+ if err != nil {
457+ return fmt .Errorf ("Failed to run iptables command: %s" , err .Error ())
458+ }
459+ if ! exists {
460+ err := iptablesCmdHandler .Insert ("filter" , ipvsFirewallChainName , 1 , args ... )
461+ if err != nil {
462+ return fmt .Errorf ("Failed to run iptables command: %s" , err .Error ())
400463 }
401- set := fmt .Sprintf ("%s,%s:%d" , ipvsSvc .Address .String (), protocol , ipvsSvc .Port )
402- svcSets = append (svcSets , set )
403464 }
404465
405- err = svcIpSet .Refresh (svcSets , utils .OptionTimeout , "0" )
466+ comment = "allow icmp echo requests to service IPs"
467+ args = []string {"-m" , "comment" , "--comment" , comment ,
468+ "-p" , "icmp" , "--icmp-type" , "echo-request" ,
469+ "-j" , "ACCEPT" }
470+ err = iptablesCmdHandler .AppendUnique ("filter" , ipvsFirewallChainName , args ... )
406471 if err != nil {
407- return fmt .Errorf ("failed to sync ipset : %s" , err .Error ())
472+ return fmt .Errorf ("Failed to run iptables command : %s" , err .Error ())
408473 }
409474
410- // Add iptables rule to allow input traffic to ipvs services
411- iptablesCmdHandler , err := iptables .New ()
475+ // We exclude the local addresses here as that would otherwise block all
476+ // traffic to local addresses if any NodePort service exists.
477+ comment = "reject all unexpected traffic to service IPs"
478+ args = []string {"-m" , "comment" , "--comment" , comment ,
479+ "-m" , "set" , "!" , "--match-set" , localIPsIPSetName , "dst" ,
480+ "-j" , "REJECT" , "--reject-with" , "icmp-port-unreachable" }
481+ err = iptablesCmdHandler .AppendUnique ("filter" , ipvsFirewallChainName , args ... )
412482 if err != nil {
413- return errors . New ("Failed to initialize iptables executor" + err .Error ())
483+ return fmt . Errorf ("Failed to run iptables command: %s" , err .Error ())
414484 }
415485
416- comment := "allow input traffic to ipvs services"
417- args := [] string { "-m" , "comment" , "--comment" , comment , "-m" , "set" , "--match-set" , svcIpSetName , "dst,dst" , "-j" , "ACCEPT" }
418- exists , err : = iptablesCmdHandler .Exists ("filter" , "INPUT" , args ... )
486+ // Pass incomming traffic into our custom chain.
487+ ipvsFirewallInputChainRule := getIpvsFirewallInputChainRule ()
488+ exists , err = iptablesCmdHandler .Exists ("filter" , "INPUT" , ipvsFirewallInputChainRule ... )
419489 if err != nil {
420490 return fmt .Errorf ("Failed to run iptables command: %s" , err .Error ())
421491 }
422492 if ! exists {
423- err : = iptablesCmdHandler .Insert ("filter" , "INPUT" , 1 , args ... )
493+ err = iptablesCmdHandler .Insert ("filter" , "INPUT" , 1 , ipvsFirewallInputChainRule ... )
424494 if err != nil {
425495 return fmt .Errorf ("Failed to run iptables command: %s" , err .Error ())
426496 }
@@ -429,6 +499,114 @@ func (nsc *NetworkServicesController) setupIpvsFirewall() error {
429499 return nil
430500}
431501
502+ func (nsc * NetworkServicesController ) cleanupIpvsFirewall () {
503+ /*
504+ - delete firewall rules
505+ - delete ipsets
506+ */
507+ var err error
508+
509+ // Clear iptables rules.
510+ iptablesCmdHandler , err := iptables .New ()
511+ if err != nil {
512+ glog .Errorf ("Failed to initialize iptables executor: %s" , err .Error ())
513+ } else {
514+ ipvsFirewallInputChainRule := getIpvsFirewallInputChainRule ()
515+ err = iptablesCmdHandler .Delete ("filter" , "INPUT" , ipvsFirewallInputChainRule ... )
516+ if err != nil {
517+ glog .Errorf ("Failed to run iptables command: %s" , err .Error ())
518+ }
519+
520+ err = iptablesCmdHandler .ClearChain ("filter" , ipvsFirewallChainName )
521+ if err != nil {
522+ glog .Errorf ("Failed to run iptables command: %s" , err .Error ())
523+ }
524+
525+ err = iptablesCmdHandler .DeleteChain ("filter" , ipvsFirewallChainName )
526+ if err != nil {
527+ glog .Errorf ("Failed to run iptables command: %s" , err .Error ())
528+ }
529+ }
530+
531+ // Clear ipsets.
532+ ipSetHandler , err := utils .NewIPSet (false )
533+ if err != nil {
534+ glog .Errorf ("Failed to initialize ipset handler: %s" , err .Error ())
535+ } else {
536+ err = ipSetHandler .Destroy (localIPsIPSetName )
537+ if err != nil {
538+ glog .Errorf ("failed to destroy ipset: %s" , err .Error ())
539+ }
540+
541+ err = ipSetHandler .Destroy (serviceIPsIPSetName )
542+ if err != nil {
543+ glog .Errorf ("failed to destroy ipset: %s" , err .Error ())
544+ }
545+
546+ err = ipSetHandler .Destroy (ipvsServicesIPSetName )
547+ if err != nil {
548+ glog .Errorf ("failed to destroy ipset: %s" , err .Error ())
549+ }
550+ }
551+ }
552+
553+ func (nsc * NetworkServicesController ) syncIpvsFirewall () error {
554+ /*
555+ - update ipsets based on currently active IPVS services
556+ */
557+ var err error
558+
559+ localIPsIPSet := nsc .ipsetMap [localIPsIPSetName ]
560+
561+ // Populate local addresses ipset.
562+ addrs , err := getAllLocalIPs ()
563+ localIPsSets := make ([]string , 0 , len (addrs ))
564+ for _ , addr := range addrs {
565+ localIPsSets = append (localIPsSets , addr .IP .String ())
566+ }
567+ err = localIPsIPSet .Refresh (localIPsSets , utils .OptionTimeout , "0" )
568+ if err != nil {
569+ return fmt .Errorf ("failed to sync ipset: %s" , err .Error ())
570+ }
571+
572+ // Populate service ipsets.
573+ ipvsServices , err := nsc .ln .ipvsGetServices ()
574+ if err != nil {
575+ return errors .New ("Failed to list IPVS services: " + err .Error ())
576+ }
577+
578+ serviceIPsSets := make ([]string , 0 , len (ipvsServices ))
579+ ipvsServicesSets := make ([]string , 0 , len (ipvsServices ))
580+
581+ for _ , ipvsService := range ipvsServices {
582+ protocol := "udp"
583+ if ipvsService .Protocol == syscall .IPPROTO_TCP {
584+ protocol = "tcp"
585+ }
586+
587+ serviceIPsSet := ipvsService .Address .String ()
588+ serviceIPsSets = append (serviceIPsSets , serviceIPsSet )
589+
590+ ipvsServicesSet := fmt .Sprintf ("%s,%s:%d" , ipvsService .Address .String (), protocol , ipvsService .Port )
591+ ipvsServicesSets = append (ipvsServicesSets , ipvsServicesSet )
592+
593+ }
594+
595+ serviceIPsIPSet := nsc .ipsetMap [serviceIPsIPSetName ]
596+ err = serviceIPsIPSet .Refresh (serviceIPsSets , utils .OptionTimeout , "0" )
597+ if err != nil {
598+ return fmt .Errorf ("failed to sync ipset: %s" , err .Error ())
599+ }
600+
601+ ipvsServicesIPSet := nsc .ipsetMap [ipvsServicesIPSetName ]
602+ err = ipvsServicesIPSet .Refresh (ipvsServicesSets , utils .OptionTimeout , "0" )
603+ if err != nil {
604+ return fmt .Errorf ("failed to sync ipset: %s" , err .Error ())
605+ }
606+
607+ return nil
608+ }
609+
432610func (nsc * NetworkServicesController ) publishMetrics (serviceInfoMap serviceInfoMap ) error {
433611 start := time .Now ()
434612 defer func () {
@@ -952,7 +1130,7 @@ func (nsc *NetworkServicesController) syncIpvsServices(serviceInfoMap serviceInf
9521130 }
9531131 }
9541132
955- err = nsc .setupIpvsFirewall ()
1133+ err = nsc .syncIpvsFirewall ()
9561134 if err != nil {
9571135 glog .Errorf ("Error syncing ipvs svc iptable rules: %s" , err .Error ())
9581136 }
@@ -2061,6 +2239,8 @@ func (nsc *NetworkServicesController) Cleanup() {
20612239 return
20622240 }
20632241
2242+ nsc .cleanupIpvsFirewall ()
2243+
20642244 // delete dummy interface used to assign cluster IP's
20652245 dummyVipInterface , err := netlink .LinkByName (KUBE_DUMMY_IF )
20662246 if err != nil {
0 commit comments