diff --git a/README.md b/README.md index cb6454af..aaad1d9d 100644 --- a/README.md +++ b/README.md @@ -362,6 +362,15 @@ Now let's access local service with header `"a: 1"` Hello world! ``` +If you want cancel proxy, just run command: + +```shell +➜ ~ kubevpn leave deployments/productpage +leave workload deployments/productpage +workload default/deployments/productpage is controlled by a controller +leave workload deployments/productpage successfully +``` + ### Dev mode in local Docker 🐳 Run the Kubernetes pod in the local Docker container, and cooperate with the service mesh to intercept the traffic with diff --git a/README_ZH.md b/README_ZH.md index 59febe0b..5e959f0a 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -292,6 +292,15 @@ create remote inbound pod for deployment/productpage successfully Hello world!% ``` +如果你需要取消代理流量,可以执行如下命令: + +```shell +➜ ~ kubevpn leave deployments/productpage +leave workload deployments/productpage +workload default/deployments/productpage is controlled by a controller +leave workload deployments/productpage successfully +``` + ### 本地进入开发模式 🐳 将 Kubernetes pod 运行在本地的 Docker 容器中,同时配合 service mesh, 拦截带有指定 header 的流量到本地,或者所有的流量到本地。这个开发模式依赖于本地 Docker。 diff --git a/cmd/kubevpn/cmds/clone.go b/cmd/kubevpn/cmds/clone.go index 2e70b81b..eccaf78d 100644 --- a/cmd/kubevpn/cmds/clone.go +++ b/cmd/kubevpn/cmds/clone.go @@ -130,7 +130,7 @@ func CmdClone(f cmdutil.Factory) *cobra.Command { return nil }, } - cmd.Flags().StringToStringVarP(&options.Headers, "headers", "H", map[string]string{}, "Traffic with special headers with reverse it to clone workloads, you should startup your service after reverse workloads successfully, If not special, redirect all traffic to clone workloads, format is k=v, like: k1=v1,k2=v2") + cmd.Flags().StringToStringVarP(&options.Headers, "headers", "H", map[string]string{}, "Traffic with special headers (use `and` to match all headers) with reverse it to local PC, If not special, redirect all traffic to local PC. eg: --headers a=1 --headers b=2") 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(&options.ExtraCIDR, "extra-cidr", []string{}, "Extra cidr string, eg: --extra-cidr 192.168.0.159/24 --extra-cidr 192.168.1.160/32") diff --git a/cmd/kubevpn/cmds/proxy.go b/cmd/kubevpn/cmds/proxy.go index 33a0aa21..c0223057 100644 --- a/cmd/kubevpn/cmds/proxy.go +++ b/cmd/kubevpn/cmds/proxy.go @@ -45,6 +45,9 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command { # Reverse proxy with mesh, traffic with header a=1, will hit local PC, otherwise no effect kubevpn proxy service/productpage --headers a=1 + + # Reverse proxy with mesh, traffic with header a=1 and b=2, will hit local PC, otherwise no effect + kubevpn proxy service/productpage --headers a=1 --headers b=2 # Connect to api-server behind of bastion host or ssh jump host and proxy kubernetes resource traffic into local PC kubevpn proxy deployment/productpage --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem --headers a=1 @@ -146,7 +149,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command { return nil }, } - cmd.Flags().StringToStringVarP(&connect.Headers, "headers", "H", map[string]string{}, "Traffic with special headers with reverse it to local PC, you should startup your service after reverse workloads successfully, If not special, redirect all traffic to local PC, format is k=v, like: k1=v1,k2=v2") + cmd.Flags().StringToStringVarP(&connect.Headers, "headers", "H", map[string]string{}, "Traffic with special headers (use `and` to match all headers) with reverse it to local PC, If not special, redirect all traffic to local PC. eg: --headers a=1 --headers b=2") 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") diff --git a/pkg/handler/envoy.go b/pkg/handler/envoy.go index c62e03b7..295e6098 100644 --- a/pkg/handler/envoy.go +++ b/pkg/handler/envoy.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "reflect" "strings" "time" @@ -199,43 +200,8 @@ func addEnvoyConfig(mapInterface v12.ConfigMapInterface, nodeID string, tunIP ut return err } } - var index = -1 - for i, virtual := range v { - if nodeID == virtual.Uid { - index = i - break - } - } - if index < 0 { - v = append(v, &controlplane.Virtual{ - Uid: nodeID, - Ports: port, - Rules: []*controlplane.Rule{{ - Headers: headers, - LocalTunIPv4: tunIP.LocalTunIPv4, - LocalTunIPv6: tunIP.LocalTunIPv6, - }}, - }) - } else { - var found bool - for j, rule := range v[index].Rules { - if rule.LocalTunIPv4 == tunIP.LocalTunIPv4 && - rule.LocalTunIPv6 == tunIP.LocalTunIPv6 { - found = true - v[index].Rules[j].Headers = util.Merge[string, string](v[index].Rules[j].Headers, headers) - } - } - if !found { - v[index].Rules = append(v[index].Rules, &controlplane.Rule{ - Headers: headers, - LocalTunIPv4: tunIP.LocalTunIPv4, - LocalTunIPv6: tunIP.LocalTunIPv6, - }) - if v[index].Ports == nil { - v[index].Ports = port - } - } - } + + v = addVirtualRule(v, nodeID, port, headers, tunIP) marshal, err := yaml.Marshal(v) if err != nil { @@ -246,6 +212,57 @@ func addEnvoyConfig(mapInterface v12.ConfigMapInterface, nodeID string, tunIP ut return err } +func addVirtualRule(v []*controlplane.Virtual, nodeID string, port []v1.ContainerPort, headers map[string]string, tunIP util.PodRouteConfig) []*controlplane.Virtual { + var index = -1 + for i, virtual := range v { + if nodeID == virtual.Uid { + index = i + break + } + } + // 1) if not found uid, means nobody proxying it, just add it + if index < 0 { + return append(v, &controlplane.Virtual{ + Uid: nodeID, + Ports: port, + Rules: []*controlplane.Rule{{ + Headers: headers, + LocalTunIPv4: tunIP.LocalTunIPv4, + LocalTunIPv6: tunIP.LocalTunIPv6, + }}, + }) + } + + // 2) if already proxy deployment/xxx with header a=1. also want to add b=2 + for j, rule := range v[index].Rules { + if rule.LocalTunIPv4 == tunIP.LocalTunIPv4 && + rule.LocalTunIPv6 == tunIP.LocalTunIPv6 { + v[index].Rules[j].Headers = util.Merge[string, string](v[index].Rules[j].Headers, headers) + return v + } + } + + // 3) if already proxy deployment/xxx with header a=1, other user can replace it to self + for j, rule := range v[index].Rules { + if reflect.DeepEqual(rule.Headers, headers) { + v[index].Rules[j].LocalTunIPv6 = tunIP.LocalTunIPv6 + v[index].Rules[j].LocalTunIPv4 = tunIP.LocalTunIPv4 + return v + } + } + + // 4) if header is not same and tunIP is not same, means another users, just add it + v[index].Rules = append(v[index].Rules, &controlplane.Rule{ + Headers: headers, + LocalTunIPv4: tunIP.LocalTunIPv4, + LocalTunIPv6: tunIP.LocalTunIPv6, + }) + if v[index].Ports == nil { + v[index].Ports = port + } + return v +} + func removeEnvoyConfig(mapInterface v12.ConfigMapInterface, nodeID string, localTunIPv4 string) (bool, error) { configMap, err := mapInterface.Get(context.Background(), config.ConfigMapPodTrafficManager, metav1.GetOptions{}) if k8serrors.IsNotFound(err) {