forked from kube-vip/kube-vip
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds the initial code for egress (SNAT).
- Loading branch information
Showing
7 changed files
with
488 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package egress | ||
|
||
//https://github.com/Trojan295/kube-router/commit/d48fd0a275249eb44e272d7f936ac91610c987cd#diff-3b65e4098d69eede2c4abfedc10116dda8fa05b9e308c18c1cb62b1a3fc8c119 | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"fmt" | ||
"net" | ||
"os" | ||
"os/exec" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/coreos/go-iptables/iptables" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const ( | ||
preroutingMarkChain = "CHINCHILLA-PREROUTING-MARK" | ||
postroutingSnatChain = "CHINCHILLA-POSTROUTING-SNAT" | ||
|
||
egressIPAnnotation = "egressSNAT.IPAddress" | ||
egressFixedPortsAnnotation = "egressSNAT.FixedPorts" | ||
|
||
routingTableFile = "/opt/rt_tables" | ||
) | ||
|
||
func iptablesChainExists(table string, chain string, it *iptables.IPTables) (bool, error) { | ||
chains, err := it.ListChains(table) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
for _, c := range chains { | ||
if c == chain { | ||
return true, nil | ||
} | ||
} | ||
return false, nil | ||
} | ||
|
||
func iptablesEnsureChain(table string, chain string, it *iptables.IPTables) error { | ||
if exists, err := iptablesChainExists(table, chain, it); err != nil { | ||
return nil | ||
} else if !exists { | ||
return it.NewChain(table, chain) | ||
} | ||
return nil | ||
} | ||
|
||
func iptablesEnsureRuleAtPosition(table, chain string, position int, it *iptables.IPTables, rule ...string) error { | ||
if exists, err := it.Exists(table, chain, rule...); err != nil { | ||
return err | ||
} else if exists { | ||
if err2 := it.Delete(table, chain, rule...); err2 != nil { | ||
return err2 | ||
} | ||
} | ||
|
||
return it.Insert(table, chain, position, rule...) | ||
} | ||
|
||
type routeTable struct { | ||
ID int | ||
Name string | ||
Subnet net.IPNet | ||
} | ||
|
||
func FindRouteTableForIP(ip net.IP, rts []routeTable) *routeTable { | ||
for _, rt := range rts { | ||
if rt.Subnet.Contains(ip) { | ||
return &rt | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func EnsureRouteRule(rt *routeTable) error { | ||
fwmark := fmt.Sprintf("%x/0xff", rt.ID) | ||
|
||
out, err := exec.Command("ip", "rule", "show", "fwmark", fwmark, "table", rt.Name).Output() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if string(out) != "" { | ||
return nil | ||
} | ||
|
||
_, err = exec.Command("ip", "rule", "add", "fwmark", fwmark, "table", rt.Name).Output() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func GetRouteTables() ([]routeTable, error) { | ||
tables := make([]routeTable, 0) | ||
|
||
fp, err := os.Open(routingTableFile) | ||
if err != nil { | ||
return tables, err | ||
} | ||
defer fp.Close() | ||
|
||
r := bufio.NewScanner(fp) | ||
for r.Scan() { | ||
line := strings.Trim(r.Text(), " ") | ||
if strings.HasPrefix(line, "#") { | ||
continue | ||
} | ||
|
||
cols := strings.Fields(line) | ||
name := cols[1] | ||
ID, err := strconv.Atoi(cols[0]) | ||
if err != nil { | ||
log.Error("invalid route table entry in /etc/iproute2/rt_tables") | ||
continue | ||
} | ||
|
||
rt := routeTable{ | ||
ID: ID, | ||
Name: name, | ||
} | ||
|
||
if rt.ID == 0 { | ||
continue | ||
} | ||
|
||
cidr, err := getCIDRForRouteTable(&rt) | ||
if err != nil || cidr == nil { | ||
continue | ||
} | ||
|
||
rt.Subnet = *cidr | ||
|
||
tables = append(tables, rt) | ||
} | ||
|
||
return tables, nil | ||
} | ||
|
||
func getCIDRForRouteTable(rt *routeTable) (*net.IPNet, error) { | ||
tableID := fmt.Sprintf("%d", rt.ID) | ||
|
||
out, err := exec.Command("ip", "rule", "show", "table", tableID).Output() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
r := bufio.NewScanner(bytes.NewBuffer(out)) | ||
|
||
var cidr *net.IPNet = nil | ||
|
||
pattern := fmt.Sprintf(`\d+:.+from (.+) lookup.+`) | ||
re := regexp.MustCompile(pattern) | ||
|
||
for r.Scan() { | ||
line := r.Text() | ||
result := re.FindStringSubmatch(line) | ||
|
||
if len(result) > 0 { | ||
_, cidr, _ = net.ParseCIDR(result[1]) | ||
if cidr != nil { | ||
break | ||
} | ||
} | ||
} | ||
|
||
return cidr, nil | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package manager | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/kube-vip/kube-vip/pkg/vip" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func (sm *Manager) configureEgress(vipIP, podIP string) error { | ||
serviceCIDR, podCIDR, err := sm.AutoDiscoverCIDRs() | ||
if err != nil { | ||
serviceCIDR = "10.96.0.0/12" | ||
podCIDR = "10.0.0.0/16" | ||
} | ||
i, err := vip.CreateIptablesClient() | ||
if err != nil { | ||
return fmt.Errorf("error Creating iptables client [%s]", err) | ||
} | ||
// Check if the kube-vip mangle chain exists, if not create it | ||
exists, err := i.CheckMangleChain(vip.MangleChainName) | ||
if err != nil { | ||
return fmt.Errorf("error checking for existence of mangle chain [%s], error [%s]", vip.MangleChainName, err) | ||
} | ||
if !exists { | ||
err = i.CreateMangleChain(vip.MangleChainName) | ||
if err != nil { | ||
return fmt.Errorf("error creating mangle chain [%s], error [%s]", vip.MangleChainName, err) | ||
} | ||
} | ||
err = i.AppendReturnRulesForDestinationSubnet(vip.MangleChainName, podCIDR) | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = i.AppendReturnRulesForDestinationSubnet(vip.MangleChainName, serviceCIDR) | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = i.AppendReturnRulesForMarking(vip.MangleChainName, podIP+"/32") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
err = i.InsertMangeTableIntoPrerouting(vip.MangleChainName) | ||
if err != nil { | ||
panic(err) | ||
} | ||
err = i.InsertSourceNat(vipIP, podIP) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
_ = i.DumpChain(vip.MangleChainName) | ||
err = vip.DeleteExistingSessions(podIP) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (sm *Manager) AutoDiscoverCIDRs() (serviceCIDR, podCIDR string, err error) { | ||
pod, err := sm.clientSet.CoreV1().Pods("kube-system").Get(context.TODO(), "kube-controller-manager", v1.GetOptions{}) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
for flags := range pod.Spec.Containers[0].Command { | ||
if strings.Contains(pod.Spec.Containers[0].Command[flags], "--cluster-cidr=") { | ||
podCIDR = strings.ReplaceAll(pod.Spec.Containers[0].Command[flags], "--cluster-cidr=", "") | ||
} | ||
if strings.Contains(pod.Spec.Containers[0].Command[flags], "--service-cluster-ip-range=") { | ||
serviceCIDR = strings.ReplaceAll(pod.Spec.Containers[0].Command[flags], "--service-cluster-ip-range=", "") | ||
} | ||
} | ||
if podCIDR == "" || serviceCIDR == "" { | ||
err = fmt.Errorf("unable to fully determine cluster CIDR configurations") | ||
} | ||
|
||
return | ||
} | ||
|
||
func TeardownEgress(podIP, serviceIP string) error { | ||
i, err := vip.CreateIptablesClient() | ||
if err != nil { | ||
return fmt.Errorf("error Creating iptables client [%s]", err) | ||
} | ||
return i.DeleteSourceNat(podIP, serviceIP) | ||
} |
Oops, something went wrong.