diff --git a/cmd/kubevpn/cmds/connect.go b/cmd/kubevpn/cmds/connect.go index 8a389b81..ed1a4cc2 100644 --- a/cmd/kubevpn/cmds/connect.go +++ b/cmd/kubevpn/cmds/connect.go @@ -64,6 +64,7 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command { cmd.Flags().BoolVar(&config.Debug, "debug", false, "enable debug mode or not, true or false") cmd.Flags().StringVar(&config.Image, "image", config.Image, "use this image to startup container") cmd.Flags().StringArrayVar(&connect.ExtraCIDR, "extra-cidr", []string{}, "Extra cidr string, eg: --extra-cidr 192.168.0.159/24 --extra-cidr 192.168.1.160/32") + cmd.Flags().StringArrayVar(&connect.ExtraDomain, "extra-domain", []string{}, "Extra domain string, the resolved ip will add to route table, eg: --extra-domain test.abc.com --extra-domain foo.test.com") addSshFlag(cmd, sshConf) return cmd diff --git a/cmd/kubevpn/cmds/dev.go b/cmd/kubevpn/cmds/dev.go index e1dfabc3..2a98f027 100644 --- a/cmd/kubevpn/cmds/dev.go +++ b/cmd/kubevpn/cmds/dev.go @@ -84,9 +84,10 @@ Startup your kubernetes workloads in local Docker container with same volume、e }, RunE: func(cmd *cobra.Command, args []string) error { connect := handler.ConnectOptions{ - Headers: devOptions.Headers, - Workloads: args, - ExtraCIDR: devOptions.ExtraCIDR, + Headers: devOptions.Headers, + Workloads: args, + ExtraCIDR: devOptions.ExtraCIDR, + ExtraDomain: devOptions.ExtraDomain, } mode := container.NetworkMode(devOptions.NetMode.NetworkMode()) @@ -154,6 +155,7 @@ Startup your kubernetes workloads in local Docker container with same volume、e cmdutil.AddContainerVarFlags(cmd, &devOptions.ContainerName, devOptions.ContainerName) cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", completion.ContainerCompletionFunc(f))) cmd.Flags().StringArrayVar(&devOptions.ExtraCIDR, "extra-cidr", []string{}, "Extra cidr string, eg: --extra-cidr 192.168.0.159/24 --extra-cidr 192.168.1.160/32") + cmd.Flags().StringArrayVar(&devOptions.ExtraDomain, "extra-domain", []string{}, "Extra domain string, the resolved ip will add to route table, eg: --extra-domain test.abc.com --extra-domain foo.test.com") // docker options cmd.Flags().Var(&devOptions.ExtraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") diff --git a/cmd/kubevpn/cmds/duplicate.go b/cmd/kubevpn/cmds/duplicate.go index 88a24b20..b763e61e 100644 --- a/cmd/kubevpn/cmds/duplicate.go +++ b/cmd/kubevpn/cmds/duplicate.go @@ -116,6 +116,7 @@ func CmdDuplicate(f cmdutil.Factory) *cobra.Command { cmd.Flags().BoolVar(&config.Debug, "debug", false, "Enable debug mode or not, true or false") cmd.Flags().StringVar(&config.Image, "image", config.Image, "Use this image to startup container") cmd.Flags().StringArrayVar(&duplicateOptions.ExtraCIDR, "extra-cidr", []string{}, "Extra cidr string, eg: --extra-cidr 192.168.0.159/24 --extra-cidr 192.168.1.160/32") + cmd.Flags().StringArrayVar(&duplicateOptions.ExtraDomain, "extra-domain", []string{}, "Extra domain string, the resolved ip will add to route table, eg: --extra-domain test.abc.com --extra-domain foo.test.com") cmd.Flags().StringVar(&duplicateOptions.TargetImage, "target-image", "", "Duplicate container use this image to startup container, if not special, use origin origin image") cmd.Flags().StringVar(&duplicateOptions.TargetContainer, "target-container", "", "Duplicate container use special image to startup this container, if not special, use origin origin image") diff --git a/cmd/kubevpn/cmds/proxy.go b/cmd/kubevpn/cmds/proxy.go index 3a60c55d..0e2620f0 100644 --- a/cmd/kubevpn/cmds/proxy.go +++ b/cmd/kubevpn/cmds/proxy.go @@ -93,6 +93,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command { cmd.Flags().BoolVar(&config.Debug, "debug", false, "Enable debug mode or not, true or false") cmd.Flags().StringVar(&config.Image, "image", config.Image, "Use this image to startup container") cmd.Flags().StringArrayVar(&connect.ExtraCIDR, "extra-cidr", []string{}, "Extra cidr string, eg: --extra-cidr 192.168.0.159/24 --extra-cidr 192.168.1.160/32") + cmd.Flags().StringArrayVar(&connect.ExtraDomain, "extra-domain", []string{}, "Extra domain string, the resolved ip will add to route table, eg: --extra-domain test.abc.com --extra-domain foo.test.com") addSshFlag(cmd, sshConf) cmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f) diff --git a/pkg/dev/main.go b/pkg/dev/main.go index 4f791ed5..4f48f013 100644 --- a/pkg/dev/main.go +++ b/pkg/dev/main.go @@ -40,6 +40,7 @@ type Options struct { ContainerName string NoProxy bool ExtraCIDR []string + ExtraDomain []string // docker options Platform string diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go index 8759713e..f9d461f1 100644 --- a/pkg/dns/dns.go +++ b/pkg/dns/dns.go @@ -33,7 +33,7 @@ func GetDNSServiceIPFromPod(clientset *kubernetes.Clientset, restclient *rest.RE if err != nil { return nil, err } - if ips, err := getDNSIPFromDnsPod(clientset); err == nil && len(ips) != 0 { + if ips, err := GetDNSIPFromDnsPod(clientset); err == nil && len(ips) != 0 { resolvConf.Servers = ips } @@ -45,7 +45,7 @@ func GetDNSServiceIPFromPod(clientset *kubernetes.Clientset, restclient *rest.RE return resolvConf, nil } -func getDNSIPFromDnsPod(clientset *kubernetes.Clientset) (ips []string, err error) { +func GetDNSIPFromDnsPod(clientset *kubernetes.Clientset) (ips []string, err error) { var serviceList *v12.ServiceList serviceList, err = clientset.CoreV1().Services(v1.NamespaceSystem).List(context.Background(), v1.ListOptions{ LabelSelector: fields.OneTermEqualSelector("k8s-app", "kube-dns").String(), diff --git a/pkg/handler/connect.go b/pkg/handler/connect.go index 655db8eb..6c11fc07 100644 --- a/pkg/handler/connect.go +++ b/pkg/handler/connect.go @@ -13,7 +13,9 @@ import ( "time" "github.com/containernetworking/cni/pkg/types" + "github.com/google/gopacket/routing" netroute "github.com/libp2p/go-netroute" + miekgdns "github.com/miekg/dns" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/pflag" @@ -47,10 +49,11 @@ import ( ) type ConnectOptions struct { - Namespace string - Headers map[string]string - Workloads []string - ExtraCIDR []string + Namespace string + Headers map[string]string + Workloads []string + ExtraCIDR []string + ExtraDomain []string clientset *kubernetes.Clientset restclient *rest.RESTClient @@ -149,6 +152,10 @@ func (c *ConnectOptions) DoConnect() (err error) { return err } go c.heartbeats() + err = c.addExtraRoute(ctx) + if err != nil { + return err + } log.Info("dns service ok") return } @@ -724,3 +731,65 @@ func (c *ConnectOptions) heartbeats() { }() } } + +func (c *ConnectOptions) addExtraRoute(ctx context.Context) (err error) { + if len(c.ExtraDomain) == 0 { + return + } + var ips []string + ips, err = dns.GetDNSIPFromDnsPod(c.clientset) + if err != nil { + return + } + if len(ips) == 0 { + err = fmt.Errorf("can't found any dns server") + return + } + + var r routing.Router + r, err = netroute.New() + if err != nil { + return + } + + var tunIface *net.Interface + tunIface, err = tun.GetInterface() + if err != nil { + return + } + + addRouteFunc := func(resource, ip string) { + if ip == "" || net.ParseIP(ip) == nil { + return + } + // if route is right, not need add route + iface, _, _, errs := r.Route(net.ParseIP(ip)) + if errs == nil && tunIface.Name == iface.Name { + return + } + errs = tun.AddRoutes(types.Route{Dst: net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(32, 32)}}) + if errs != nil { + log.Debugf("[route] add route failed, domain: %s, ip: %s,err: %v", resource, ip, err) + } + } + + client := &miekgdns.Client{Net: "udp", SingleInflight: true, DialTimeout: time.Second * 30} + for _, domain := range c.ExtraDomain { + var answer *miekgdns.Msg + answer, _, err = client.ExchangeContext(ctx, &miekgdns.Msg{ + Question: []miekgdns.Question{{ + Name: domain + ".", + Qtype: miekgdns.TypeA, + }}, + }, fmt.Sprintf("%s:%d", ips[0], 53)) + if err != nil { + return + } + for _, rr := range answer.Answer { + if a, ok := rr.(*miekgdns.A); ok && a.A != nil { + addRouteFunc(domain, a.A.String()) + } + } + } + return +} diff --git a/pkg/handler/duplicate.go b/pkg/handler/duplicate.go index 8ad38a55..3dd44728 100644 --- a/pkg/handler/duplicate.go +++ b/pkg/handler/duplicate.go @@ -40,10 +40,11 @@ import ( ) type DuplicateOptions struct { - Namespace string - Headers map[string]string - Workloads []string - ExtraCIDR []string + Namespace string + Headers map[string]string + Workloads []string + ExtraCIDR []string + ExtraDomain []string TargetKubeconfig string TargetNamespace string