Compare commits

...

239 Commits

Author SHA1 Message Date
fengcaiwen
bbd5e121e5 refactor: add prefix to log 2025-12-23 21:46:53 +08:00
naison
1fe1e29f72 chore: upgrade github action 2025-12-18 11:53:35 +08:00
kubenetworks
2dad0ef766 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-12-05 17:36:15 +08:00
wencaiwulue
c45f5be4ea feat: update krew index version to refs/tags/v2.9.11 2025-12-05 17:36:04 +08:00
Roman Martsev
29d4b6d314 chore: increase namespace list limit from 100 to 500 2025-12-05 14:35:04 +08:00
fengcaiwen
903487b209 hotfix: close tcp conn after handle 2025-11-16 17:30:06 +08:00
naison
abaa9e474e refactor: upgrade logic 2025-11-16 17:29:12 +08:00
kubenetworks
871fec10c3 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-10-22 11:51:50 +08:00
wencaiwulue
577ce07c37 feat: update krew index version to refs/tags/v2.9.10 2025-10-22 11:51:27 +08:00
fengcaiwen
0eafa3402c feat: upgrade envoy version to 1.36.2 2025-10-22 11:13:45 +08:00
huzijun
b2a9d9fc2a fix:fix fix ipv4_tcp_timestamps 2025-10-22 11:11:18 +08:00
fengcaiwen
e85b8b7451 fix: fix interrupt proxy mode but not rollout deploy bug 2025-10-22 10:54:52 +08:00
fengcaiwen
cc1f08033e feat: show tun ip for cmd status 2025-10-21 20:13:23 +08:00
fengcaiwen
b0bcc16f5f hotfix: fix interrupt proxy mode but not rollout deploy 2025-10-21 15:29:29 +08:00
fengcaiwen
e830a28581 fix: fix bugs 2025-10-21 12:35:16 +08:00
naison
cf5f0b4e88 refactor: remove iptables MASQUERADE 2025-10-20 10:31:52 +08:00
fengcaiwen
66eca5fdce hotfix: cmd once retry restart deploy if conflict 2025-10-11 17:29:45 +08:00
fengcaiwen
51e4b90f74 hotfix: keep secret && configmap && mutatingwebhookconfiguration while upgrade by helm 2025-10-11 17:29:45 +08:00
naison
811ac6d615 feat: update krew index version to refs/tags/v2.9.9 (#727)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-09-30 17:33:58 +08:00
naison
abfd4e9b86 Update charts/index.yaml (#728)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-09-30 17:33:39 +08:00
naison
adad81e7a4 fix: add index number to tun device name on windows (#726) 2025-09-30 16:41:51 +08:00
naison
61349e778f refactor: add tun device name to hosts comment (#725) 2025-09-30 16:38:02 +08:00
naison
cd7236da1b feat: add cmd route search for debug (#724) 2025-09-30 16:31:41 +08:00
naison
f6d23566d2 fix: deadlock (#723) 2025-09-30 16:29:20 +08:00
naison
00bcef1e49 refactor: show shorten kubeconfig path (#722) 2025-09-30 16:28:17 +08:00
naison
f4a0ab39f4 Update charts/index.yaml (#721)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-09-25 22:03:27 +08:00
naison
a7ebb10696 feat: update krew index version to refs/tags/v2.9.8 (#720)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-09-25 22:02:55 +08:00
naison
c62134a5eb fix: detect nameserver by send dns query (#719) 2025-09-25 21:16:46 +08:00
naison
4fe94cfd0c refactor: optimize code (#717)
* refactor: optimize code
2025-09-23 19:36:30 +08:00
naison
dd2d0f5c65 Update charts/index.yaml (#714)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-28 22:32:56 +08:00
naison
14026ea493 feat: update krew index version to refs/tags/v2.9.7 (#713)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-28 22:32:43 +08:00
naison
79fcc1b8f5 feat: add cmd route add and route delete for route management (#711) 2025-08-28 20:29:12 +08:00
naison
cf473d85c9 hotfix: add self tun ip to route table (#712) 2025-08-28 20:25:38 +08:00
naison
2d23a46d33 hotfix: fix use too much memory (#710) 2025-08-28 20:25:26 +08:00
naison
0548c5a8a0 refactor: refactor code and fix ut (#709)
* feat: ut (#707)

* feat: ut (#708)
2025-08-24 16:46:09 +08:00
naison
3a4dbe41c7 Update charts/index.yaml (#706)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-21 23:27:56 +08:00
naison
10079a541f feat: update krew index version to refs/tags/v2.9.6 (#705)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-21 23:27:38 +08:00
naison
98c4a61ca1 feat: sync mode modify api server & add ut for mode run and mode sync (#704)
* feat: mode sync modify kubeconfig apiserver to in cluster apiserver
* feat: add ut for sync mode and run mode
* fix: bugs
2025-08-21 22:45:47 +08:00
naison
4df63d1642 refactor: no drop packet (#703) 2025-08-14 19:13:17 +08:00
naison
4ddba64737 refactor: optimize code (#702) 2025-08-14 19:12:59 +08:00
naison
aef13e4c74 feat: update krew index version to refs/tags/v2.9.5 (#700)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-10 20:44:48 +08:00
naison
6e45edb077 Update charts/index.yaml (#701)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-10 20:44:31 +08:00
naison
47b2f0006c hotfix: fix --remote-kubeconfig override temp kubeconfig (#699)
* hotfix: fix --remote-kubeconfig override temp kubeconfig

* hotfix: ignore ssh set env failed

* hotfix: use unix timestamp
2025-08-10 20:05:35 +08:00
naison
f59aa8aca1 feat: update krew index version to refs/tags/v2.9.4 (#696)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-10 15:22:50 +08:00
naison
1b5ff03463 Update charts/index.yaml (#697)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-10 15:22:36 +08:00
naison
d40e69e781 feat: cmd status show remote-kubeconfig name (#695)
* feat: cmd status show remote-kubeconfig name
2025-08-10 14:23:01 +08:00
naison
d2a6d78331 feat: add flags --kubeconfig-json (#694) 2025-08-09 22:29:05 +08:00
naison
5b0758be69 Update charts/index.yaml (#693)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-07 08:13:14 +08:00
naison
9fb5d01f28 feat: update krew index version to refs/tags/v2.9.3 (#692)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-07 08:13:00 +08:00
naison
4ddff991bc hotfix: fix cmd run (#691)
* hotfix: fix cmd run

* hotfix: docs
2025-08-06 23:47:57 +08:00
naison
faecc95653 hotfix: fix cmd run (#690) 2025-08-06 23:43:26 +08:00
naison
7411deab75 Update charts/index.yaml (#689)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-06 19:37:14 +08:00
naison
1b8b6f8390 feat: update krew index version to refs/tags/v2.9.2 (#688)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-06 19:36:58 +08:00
naison
6197138ad6 refactor: refactor code (#687) 2025-08-06 18:38:40 +08:00
fengcaiwen
a13540a258 docs: add status 2025-08-05 11:12:32 +08:00
naison
3a45a33fb9 Update charts/index.yaml (#686)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-08-04 23:39:27 +08:00
naison
d49a0dc3e5 feat: update krew index version to refs/tags/v2.9.1 (#685)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-08-04 23:39:10 +08:00
naison
b3b13fce86 refactor: rename cmd dev to run (#684)
* refactor: code

* refactor: optimize code
2025-08-04 23:00:38 +08:00
naison
2d5653ee2b hotfix: ipv6 skip checksum verification (#682) 2025-08-04 21:16:53 +08:00
naison
c4d28fd497 hotfix: heartbeats with gen icmp packet (#683) 2025-08-04 21:16:38 +08:00
naison
6a82b12646 Update charts/index.yaml (#679)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-07-27 22:05:14 +08:00
naison
b2c7fb07d3 feat: update krew index version to refs/tags/v2.9.0 (#678)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-07-27 22:05:02 +08:00
naison
05905bb8ba feat: proxy mode support multiple cluster (#677)
* feat: proxy mode support multiple cluster

* feat: ut

* feat: update readme

* feat: ut

* refactor: rename

* refactor: update service
2025-07-27 21:22:08 +08:00
naison
38584da9d3 refactor: remove options netstack (#673)
* refactor: remove options netstack

* refactor: remove options netstack

* refactor: forward chain use gvisor tcp

* refactor: docs

* refactor: remove forwarder options

* refactor: optimize code

* refactor: remove node type "tcp://"

* hotfix: packet read from tun needs to handle by gvisor

* hotfix: fix charts

* refactor: remove parameter engine
2025-07-27 17:26:14 +08:00
naison
5a97a5d6e2 refactor: remove no sense code (#675) 2025-07-23 19:55:32 +08:00
naison
1823567949 hotfix: fix wait envoy rule to works takes too long time (#674) 2025-07-23 19:52:12 +08:00
naison
43023d080f Update charts/index.yaml (#671)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-07-10 11:31:41 +08:00
naison
9cd8b32c6b feat: update krew index version to refs/tags/v2.8.1 (#670)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-07-10 11:31:30 +08:00
naison
4e568fe9e3 hotfix: fix CVE (#669)
* hotfix: fix CVE

* feat: prefer use cmd rather than magic dns to set dns on linux

* feat: update go work sum

* feat: update ut
2025-07-10 10:35:48 +08:00
naison
31ead176c6 Update charts/index.yaml (#667)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-07-05 22:29:46 +08:00
naison
620a14f066 feat: update krew index version to refs/tags/v2.8.0 (#666)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-07-05 22:29:37 +08:00
naison
211c9309b2 feat: handle local conn with gvisor (#665)
* feat: handle local conn with gvisor

* feat: remove udp route map

* feat: optimize code

* feat: length

* feat: works

* feat: should works

* feat: optimize code

* feat: optimize code

* feat: gudp not set remark

* feat: ut

* feat: set to default value 0

* feat: send reset to gvisor tcp forward request if error

* feat: not need to create firewall rule on windows

* feat: typo
2025-07-05 21:43:44 +08:00
naison
62725d8011 Update charts/index.yaml (#664)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-07-04 20:54:17 +08:00
naison
8eb646dfb8 feat: update krew index version to refs/tags/v2.7.21 (#663)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-07-04 20:54:04 +08:00
naison
e490f72a78 hotfix: swap envoy rule header nil to last position (#662) 2025-07-04 19:53:16 +08:00
naison
61a33ff5bd Update charts/index.yaml (#660)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-26 12:17:02 +08:00
naison
0cdb530cbd feat: update krew index version to refs/tags/v2.7.20 (#659)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-26 12:16:45 +08:00
naison
b5235a3a26 chore: update cmd example 2025-06-26 03:24:12 +00:00
naison
f14d074417 hotfix: add cmd once for generate tls cert after helm installed (#657)
* hotfix: add cmd once for generate tls cert after helm installed

* hotfix: update scale

* hotfix: update scale

* hotfix: fix bugs

* hotfix: print

* feat: add role for get cidr

* feat: add --image options for cmd once

* feat: add role watch pod

* feat: filter api-server
2025-06-26 11:08:42 +08:00
naison
4021480ad5 docs: add donate link 2025-06-18 22:04:39 +08:00
naison
4593ddcf24 Update charts/index.yaml (#655)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-18 18:42:43 +08:00
naison
480471efc5 feat: update krew index version to refs/tags/v2.7.19 (#654)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-18 18:25:44 +08:00
mazhong
edd2450880 fix: preserve env HOME on unix (#653) 2025-06-18 17:34:36 +08:00
mazhong
b3af39f840 fix: linux startup must use sudo (#651) 2025-06-18 16:48:50 +08:00
naison
9010d05198 refactor: optimize code (#650) 2025-06-18 10:16:30 +08:00
naison
de9bbcd1b0 Update charts/index.yaml (#648)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-14 15:14:34 +08:00
naison
0fa136cb7a feat: update krew index version to refs/tags/v2.7.18 (#647)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-14 15:14:22 +08:00
naison
306ba9be8f refactor: restore logic (#646) 2025-06-14 13:30:26 +08:00
naison
6ca22822f9 feat: support restore from local config (#645)
* refactor: optimize dhcp

* feat: support recover from config

* feat: optimize code

* feat: fix bug

* feat: fix bug

* feat: rename
2025-06-14 13:01:24 +08:00
naison
33fba01904 Update charts/index.yaml (#644)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-12 14:04:12 +08:00
naison
05be5c317e feat: update krew index version to refs/tags/v2.7.17 (#643)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-12 14:04:00 +08:00
naison
46d15c5742 refactor: optimize cmd disconnect (#642) 2025-06-12 13:15:41 +08:00
naison
4949df56ef hotfix: fix dev/clone mode bug (#640) 2025-06-12 12:59:39 +08:00
naison
072e67ce6c refactor: optimize cmd disconnect (#641) 2025-06-12 12:59:08 +08:00
naison
11bb5584ea Update charts/index.yaml (#639)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-10 23:16:53 +08:00
naison
bbbcebd9d5 feat: update krew index version to refs/tags/v2.7.16 (#638)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-10 23:16:25 +08:00
naison
423254b172 Revert "chore: add sync image to ccr.ccs.tencentyun.com (#636)" 2025-06-10 22:24:10 +08:00
naison
507da8a44c feat: proxy mode use traffic-manager pod image (#635) 2025-06-10 19:02:04 +08:00
naison
bfed866c04 chore: add sync image to ccr.ccs.tencentyun.com (#636) 2025-06-10 14:55:20 +08:00
naison
bdfa4f6d16 hotfix: grpc connect to daemon process ignore http_proxy and https_proxy (#633) 2025-06-09 14:59:03 +08:00
naison
5785ba0f42 Update charts/index.yaml (#632)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-07 13:37:58 +08:00
naison
1e6475379f feat: update krew index version to refs/tags/v2.7.15 (#631)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-06-07 13:37:41 +08:00
naison
bb991fc1d7 refactor: use grpc stream send cancel operation (#629)
* refactor: use grpc stream send cancel operation

* refactor: ssh jump remove flags
2025-06-07 12:24:28 +08:00
naison
4f3e443bca feat: update 2025-06-07 12:14:56 +08:00
naison
8d64dab79f feat: update envoy image tag to v1.34.1 2025-06-07 12:14:56 +08:00
naison
2614669671 chore: spelling mistake (#628) 2025-06-04 18:01:57 +08:00
naison
2f16b31eb6 chore: add issue template (#627) 2025-06-04 17:57:03 +08:00
naison
bdeb3d6d09 Update charts/index.yaml (#626) 2025-06-04 15:17:27 +08:00
naison
ab2a8d1821 feat: update krew index version to refs/tags/v2.7.14 (#625) 2025-06-04 15:17:13 +08:00
naison
fd59ed242c refactor: use origin workload of proxy mode (#621) 2025-06-04 14:30:24 +08:00
naison
a750327d9e hotfix: ignore some known top dns server on macOS 2025-06-04 06:26:09 +00:00
naison
69c5ed6c98 ut: add cache for setup minikube on macos 2025-06-03 17:23:37 +08:00
naison
d1dd44be35 ut: setup minikube on macos 2025-06-03 16:52:44 +08:00
fengcaiwen
229eb747a4 refactor: remove target-kubeconfig of clone mode 2025-06-02 19:05:36 +08:00
kubenetworks
dd3ba1c059 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-06-01 19:35:12 +08:00
wencaiwulue
db5ea99133 feat: update krew index version to refs/tags/v2.7.13 2025-06-01 19:34:43 +08:00
naison
fcbe2d64f7 chore: add ut for center install 2025-06-01 18:42:32 +08:00
fengcaiwen
a0c0860051 hotfix: fix center install cause mutate webhook not works 2025-06-01 18:39:02 +08:00
wencaiwulue
e374d6b51d feat: update krew index version to refs/tags/v2.7.12 2025-05-23 11:46:30 +08:00
kubenetworks
9703a12bc2 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-23 11:46:18 +08:00
naison
f9f52d1001 Merge pull request #612
ut: fix ut
2025-05-23 10:53:37 +08:00
naison
51c16989fe ut: fix ut 2025-05-23 02:52:37 +00:00
naison
75c609211b refactor: use informer to list&watch pod&service ip for adding to route table (#610) 2025-05-23 10:09:06 +08:00
naison
6d545dc5c9 hotfix: remove cidr if contains api-server ip 2025-05-20 22:12:19 +08:00
naison
b17da3cbcb feat: update krew index version to refs/tags/v2.7.11 (#607) 2025-05-18 17:32:49 +08:00
naison
d1108ebd86 Update charts/index.yaml (#608) 2025-05-18 17:32:35 +08:00
naison
792839a2d4 feat: support dump service into hosts in center cluster mode (#605) 2025-05-18 16:20:34 +08:00
fengcaiwen
f493931b41 hotfix: remove job before install 2025-05-18 16:20:13 +08:00
wencaiwulue
7df065ef93 feat: update krew index version to refs/tags/v2.7.10 2025-05-14 21:23:16 +08:00
kubenetworks
c265b3581c Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-14 21:23:03 +08:00
naison
f802e03d01 hotfix: add heartbeat to manager in the pod 2025-05-14 20:22:57 +08:00
wencaiwulue
c08cb461dd feat: update krew index version to refs/tags/v2.7.9 2025-05-12 17:18:47 +08:00
kubenetworks
1a2649a02a Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-12 17:18:34 +08:00
naison
facd6bdb3d hotfix: fix create temp kubeconfig but name container path separator (#599) 2025-05-12 16:28:15 +08:00
naison
a1117dee62 hotfix: handle not found route packet with gVisor instead of drop it 2025-05-12 15:49:40 +08:00
naison
b28eaef6a7 chore(mod): upgrade purego version to v0.8.3 2025-05-12 15:47:48 +08:00
naison
46aebef01f refactor: remove temp kubeconfig before daemon quit 2025-05-12 15:46:21 +08:00
naison
3791f48737 hotfix: fix create temp kubeconfig 2025-05-12 15:32:02 +08:00
wencaiwulue
fc76b70713 feat: update krew index version to refs/tags/v2.7.8 2025-05-11 00:17:22 +08:00
kubenetworks
e990dc1d0f Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-11 00:16:45 +08:00
naison
d636449073 feat: set read/write timeout to 60s for remote tcp conn (#590) 2025-05-10 23:02:31 +08:00
fengcaiwen
e85e1a6c40 refactor: show port-forward log 2025-05-10 18:05:44 +08:00
wencaiwulue
40d09716c4 feat: update krew index version to refs/tags/v2.7.7 2025-05-09 14:44:31 +08:00
kubenetworks
63792172bd Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-09 14:44:17 +08:00
naison
ca6a2be70f refactor: optimize get pod/service netowrk cidr logic (#585) 2025-05-09 13:06:32 +08:00
naison
e21fc8cda9 hotfix: duplicated definition of symbol dlopen on go1.23.9 2025-05-09 13:04:45 +08:00
wencaiwulue
1f4698c6f8 feat: update krew index version to refs/tags/v2.7.6 2025-05-08 10:09:00 +08:00
kubenetworks
efea780edf Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-08 10:08:49 +08:00
wencaiwulue
bdb21f8964 feat: update krew index version to refs/tags/v2.7.5 2025-05-07 17:00:00 +08:00
naison
e33d2f1928 hotfix: fix init dir 2025-05-07 16:08:56 +08:00
wencaiwulue
e6df115933 feat: update krew index version to refs/tags/v2.7.5 2025-05-07 10:36:28 +08:00
kubenetworks
549e56cd05 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-07 10:36:17 +08:00
fengcaiwen
54ed2b711f hotfix: fix init dir permission deny 2025-05-07 09:12:17 +08:00
wencaiwulue
56b81574ac feat: update krew index version to refs/tags/v2.7.4 2025-05-07 08:47:19 +08:00
kubenetworks
ce2b7a010e Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-05-07 08:47:05 +08:00
fengcaiwen
5df0c3ffdc hotfix: fix init dir permission deny 2025-05-07 00:17:35 +08:00
naison
8b0e87592a hotfix: fix init dir permission deny (#573) 2025-05-07 00:01:44 +08:00
naison
31a42c1fa7 feat: update krew index version to refs/tags/v2.7.3 (#571) 2025-05-06 23:55:53 +08:00
naison
ee0957a5c9 Update charts/index.yaml (#572) 2025-05-06 23:55:40 +08:00
naison
206d74c331 feat: use dns query as port-forward health check (#570) 2025-05-06 22:20:15 +08:00
naison
53ed72dee3 Merge pull request #567 from kubenetworks/refactor/refactor-code
refactor: refactor code
2025-04-29 22:16:59 +08:00
fengcaiwen
323235f268 refactor: optimize code 2025-04-29 21:53:34 +08:00
fengcaiwen
6af6622bd3 refactor: change server log level to info 2025-04-29 21:50:08 +08:00
fengcaiwen
18ef72fc20 refactor: forward only one port 2025-04-29 21:48:14 +08:00
fengcaiwen
fe08448249 refactor: split user and root daemon log 2025-04-29 21:40:46 +08:00
fengcaiwen
ebaa4098f1 refactor: change temp kubeconfig to ~/.kubevpn/tmp 2025-04-29 21:39:45 +08:00
fengcaiwen
9ba873494f feat: add heartbeats ping pod ip 2025-04-29 21:34:58 +08:00
naison
da40f3315b Merge pull request #566 from kubenetworks/hotfix/fix-bugs
hotfix: fix bugs
2025-04-27 23:19:40 +08:00
fengcaiwen
c4540b1930 refactor: use tcp conn instead of packet conn 2025-04-27 23:03:45 +08:00
fengcaiwen
a6ec321e46 hotfix: cmp running pod image tag and client version 2025-04-27 23:02:34 +08:00
fengcaiwen
79f8aca7df hotfix: close ssh session 2025-04-27 23:02:06 +08:00
fengcaiwen
6edfc3127d hotfix: quit sudo daemon before user daemon 2025-04-27 23:01:27 +08:00
naison
bed0a9168c Update charts/index.yaml (#564)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-04-25 23:40:24 +08:00
naison
d5ee35bfa8 feat: update krew index version to refs/tags/v2.7.2 (#563)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-04-25 23:39:56 +08:00
naison
9661a122bd refactor: optimize code (#561) 2025-04-25 19:37:03 +08:00
naison
28657e3832 refactor: remove deprecated options of config flags (#560) 2025-04-24 22:44:20 +08:00
naison
6a8a197f48 hotfix: close ssh client if ctx done (#559) 2025-04-24 22:41:24 +08:00
naison
31186fc1d9 refactor: only ssh jump in user daemon (#558) 2025-04-24 22:39:03 +08:00
naison
fca3baf47e refactor: optimize code (#557) 2025-04-23 15:00:00 +08:00
naison
1cae5d270b refactor: optimize ssh logic (#555) 2025-04-21 22:19:31 +08:00
naison
a3556a263d refactor: add additional [2]byte for packet length (#554) 2025-04-21 21:51:01 +08:00
naison
dd80717d8d refactor: return error if get nil daemon client (#553) 2025-04-20 15:49:03 +08:00
naison
537b2940fe perf: route packet by each tcp conn (#548) 2025-04-19 19:14:39 +08:00
naison
9aae88d54b hotfix: set recv/send buffer size 1024k for adding ip to route table on macos (#552) 2025-04-19 15:35:43 +08:00
naison
100a8df723 refactor: revert pr #517 (#551) 2025-04-19 12:35:09 +08:00
naison
48e30b4344 refactor: use go workspace for syncthing gui (#549) 2025-04-19 12:09:06 +08:00
naison
c9f1ce6522 chore: upgrade coredns version (#550) 2025-04-19 10:06:56 +08:00
naison
c42e3475f9 Update charts/index.yaml (#547)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-04-15 23:36:27 +08:00
naison
4fb338b5fc feat: update krew index version to refs/tags/v2.7.1 (#546)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-04-15 23:36:09 +08:00
naison
15243b3935 hotfix: remove closed conn from route map (#545) 2025-04-15 21:33:41 +08:00
naison
f0f9459976 hotfix: set mtu on windows (#544) 2025-04-15 21:32:47 +08:00
naison
ee7d5fa6f9 Update charts/index.yaml (#538) 2025-04-12 14:04:52 +08:00
naison
e393f8371e feat: update krew index version to refs/tags/v2.7.0 (#537)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-04-12 14:04:23 +08:00
naison
ca333fcdaf feat: encrypt with tls 1.3 (#522) 2025-04-12 12:30:05 +08:00
naison
7e4e9e1e0d refactor: add more log detect connect namespace (#536) 2025-04-12 10:53:27 +08:00
naison
58ee2df1a3 docs: add readme.md for helm charts (#534) 2025-04-11 22:06:00 +08:00
naison
15200f1caf refactor: add more log (#533)
* feat: add more log
2025-04-11 21:12:19 +08:00
naison
23baab449c refactor: optimize code (#531) 2025-04-11 19:13:06 +08:00
naison
0ddcaa8acc hotfix: fix bug (#530) 2025-04-11 19:12:15 +08:00
naison
0c122473ce hotfix: fix parse dig command output (#529) 2025-04-11 18:50:14 +08:00
naison
d08f74a57e hotfix: optimize code (#528)
* hotfix: optimize code
2025-04-10 22:53:28 +08:00
naison
cd66bb7907 feat: add log if drop packet (#527)
* feat: add log if drop packet
2025-04-09 22:19:37 +08:00
naison
f303616554 hotfix: fix []byte leak (#525) 2025-04-09 21:08:33 +08:00
naison
3973b85d25 hotfix: remove label (#524) 2025-04-08 22:02:07 +08:00
naison
4fd1f014bd refactor: adjust log level (#523) 2025-04-08 22:01:06 +08:00
naison
fe62cf6c4d hotfix: install missing command dig (#521) 2025-04-07 13:00:13 +08:00
naison
c5900d070c Update charts/index.yaml (#520)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-04-06 21:17:57 +08:00
naison
d84ca66cfb feat: update krew index version to refs/tags/v2.6.0 (#519)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-04-06 21:17:41 +08:00
naison
60c3030e65 hotfix: use echo instead of sysctl to set ipv4 ip_forward feature (#518) 2025-04-06 18:34:34 +08:00
naison
ea574a756b feat: support gcp auth (#517)
* feat: support gcp auth
2025-04-06 17:36:10 +08:00
naison
e8735a68be refactor: optimize logic (#515)
* refactor: optimize logic
2025-04-05 21:48:18 +08:00
naison
d55d290677 feat: update krew index version to refs/tags/v2.5.1 (#513)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-04-04 00:20:33 +08:00
naison
45435bcc48 Update charts/index.yaml (#514)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-04-04 00:04:07 +08:00
naison
dbe9f91ee0 hotfix: ut (#512)
* hotfix: ut
2025-04-03 23:00:03 +08:00
naison
b3d2e1e838 refactor: optimize code (#511) 2025-04-03 21:41:35 +08:00
yuyicai
fa0b343401 feat: change dockerfile (#491)
* feat: change dockerfile

Signed-off-by: yuyicai <yuyicai@hotmail.com>

* chore: change markfile for container test

Signed-off-by: yuyicai <yuyicai@hotmail.com>

* ut

* feat: install openssl

Signed-off-by: yuyicai <yuyicai@hotmail.com>

---------

Signed-off-by: yuyicai <yuyicai@hotmail.com>
Co-authored-by: naison <895703375@qq.com>
2025-04-03 21:10:31 +08:00
naison
a1bb338cdb refactor: optimize code (#510)
* refactor: rename
2025-04-03 20:51:55 +08:00
naison
dbc9df070b feat: add options netstack to helm charts (#509) 2025-04-03 20:45:12 +08:00
naison
804708aabe feat: add options --connect-namespace to proxy and dev mode (#508) 2025-04-03 20:44:59 +08:00
naison
21087fc708 hotfix: fix upgrade bug (#507)
* hotfix: fix upgrade bug
2025-04-03 20:44:43 +08:00
naison
94db7846d8 hotfix: fix detect helm ns but still use -n namespace (#506)
* hotfix: fix detect helm ns but still use -n namespace
2025-04-02 19:20:31 +08:00
naison
e205b77e41 Update charts/index.yaml (#505)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-03-31 14:02:55 +08:00
naison
2927261390 feat: update krew index version to refs/tags/v2.5.0 (#504)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-03-31 14:02:40 +08:00
naison
8f37488207 hotfix: fix upgrade logic (#503) 2025-03-31 12:50:10 +08:00
naison
d05a53a77f Update charts/index.yaml (#502)
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
Co-authored-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-03-30 22:17:12 +08:00
naison
a2df9f7b59 feat: update krew index version to refs/tags/v2.4.3 (#501)
Co-authored-by: wencaiwulue <wencaiwulue@users.noreply.github.com>
2025-03-30 22:14:33 +08:00
naison
cd68b1fb00 hotfix: gen envoy rule id by ns and resource uid (#500)
* hotfix: gen envoy rule id by ns and uid
2025-03-30 20:57:11 +08:00
naison
208f607f03 hotfix: fix dns slow (#499) 2025-03-30 11:56:57 +08:00
naison
116a1f1983 feat: detect namespace kubevpn installed by helm (#498) 2025-03-30 11:54:40 +08:00
naison
d191c927f4 feat: add helm to go mod (#497) 2025-03-30 11:52:21 +08:00
naison
a030dc582b feat: support connect one namespace but proxy workload in another namespace (#496) 2025-03-30 11:50:11 +08:00
naison
08bcbe1611 refactor: split connect and proxy mode (#495) 2025-03-30 11:46:37 +08:00
naison
fb428403a2 hotfix: set get running pod timeout 10s to 5s (#494) 2025-03-30 11:43:37 +08:00
naison
4f4bbd79f2 chore: optimize ut (#493)
* chore: optimize ut
2025-03-25 22:33:07 +08:00
naison
1ec3ca4637 hotfix: fix clone mode bug (#492) 2025-03-24 21:53:02 +08:00
yuyicai
484a5cafe4 Merge pull request #490 from kubenetworks/chart-releaser-qqesqc5oa54qow4n
Update index.yaml
2025-03-23 21:25:11 +08:00
yuyicai
b62a6b0185 Merge pull request #489 from kubenetworks/feat/update-krew-index-version
feat: update krew index version to refs/tags/v2.4.2
2025-03-23 21:24:34 +08:00
kubenetworks
90898c8047 Update charts/index.yaml
Signed-off-by: kubenetworks <kubenetworks@users.noreply.github.com>
2025-03-23 12:53:36 +00:00
yuyicai
c06daf68e8 feat: update krew index version to refs/tags/v2.4.2 2025-03-23 12:52:53 +00:00
5406 changed files with 267188 additions and 565198 deletions

28
.github/ISSUE_TEMPLATE/01-feature.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Feature request
description: File a new feature request
labels: ["enhancement", "needs-triage"]
body:
- type: textarea
id: feature
attributes:
label: Feature description
description: Please describe the behavior you'd like to see.
validations:
required: true
- type: textarea
id: problem-usecase
attributes:
label: Problem or use case
description: Please explain which problem this would solve, or what the use case is for the feature. Keep in mind that it's more likely to be implemented if it's generally useful for a larger number of users.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives or workarounds
description: Please describe any alternatives or workarounds you have considered and, possibly, rejected.
validations:
required: true

47
.github/ISSUE_TEMPLATE/02-bug.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Bug report
description: File a new bug report
labels: ["bug", "needs-triage"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen, and any steps we might use to reproduce the problem.
placeholder: Tell us what you see!
validations:
required: true
- type: input
id: version
attributes:
label: KubeVPN client version
description: What version of KubeVPN client are you running?
placeholder: v2.7.14
validations:
required: true
- type: input
id: server
attributes:
label: KubeVPN server Image tag
description: What version of KubeVPN server image tag are you running?
placeholder: v2.7.14
validations:
required: true
- type: input
id: platform
attributes:
label: Platform & operating system
description: On what platform(s) are you seeing the problem?
placeholder: Linux arm64
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output or crash backtrace. This will be automatically formatted into code, so no need for backticks.
render: shell

View File

@@ -24,6 +24,8 @@ jobs:
uses: medyagh/setup-minikube@latest
with:
cache: true
cpus: 'max'
memory: 'max'
- name: Kubernetes info
run: |
@@ -56,8 +58,7 @@ jobs:
- name: Wait for pods reviews to be ready
run: |
kubectl wait pods -l app=reviews --for=condition=Ready --timeout=3600s
kubectl wait pods -l app=productpage --for=condition=Ready --timeout=3600s
while ! kubectl wait --for=condition=Ready pods --all --timeout=3600s; do sleep 2; done
kubectl get svc -A -o wide
kubectl get pod -A -o wide
kubectl get all -o wide

View File

@@ -31,7 +31,7 @@ jobs:
run: |
RELEASE_VERSION=${GITHUB_REF#refs/*/}
PREVERSION=$(git for-each-ref --sort='-creatordate' --format='%(refname:lstrip=2)' --count=50 'refs/tags/*' | grep -v 'rc' | awk 'NR==2')
echo ${PREVERSION}
echo ${RELEASE_VERSION}
echo ${PREVERSION}
echo "$(./.github/release-note.sh ${PREVERSION} ${RELEASE_VERSION})" > release_note.md
- name: Create Release
@@ -221,8 +221,10 @@ jobs:
--pages-branch master \
--pages-index-path charts/index.yaml \
--pr
snapcraft:
runs-on: ubuntu-24.04
needs: [ github-pages-deploy ]
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
steps:
@@ -243,6 +245,7 @@ jobs:
snapcraft upload --release=stable kubevpn_${RELEASE_VERSION}_amd64.snap
snapcraft-arm:
runs-on: ubuntu-24.04-arm
needs: [ github-pages-deploy ]
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
steps:

View File

@@ -18,9 +18,7 @@ jobs:
check-latest: true
- name: Push image to docker hub
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USER }} --password-stdin
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker buildx create --use
export VERSION=${{github.event.pull_request.head.sha}}
if [[ -z "$VERSION" ]]; then
export VERSION=${{ github.sha }}
@@ -43,6 +41,8 @@ jobs:
uses: medyagh/setup-minikube@latest
with:
cache: true
cpus: 'max'
memory: 'max'
- name: Kubernetes info
run: |
@@ -74,9 +74,7 @@ jobs:
- name: Wait for pods reviews to be ready
run: |
kubectl wait pods -l app=reviews --for=condition=Ready --timeout=3600s
kubectl wait pods -l app=productpage --for=condition=Ready --timeout=3600s
kubectl wait pods -l app=authors --for=condition=Ready --timeout=3600s
while ! kubectl wait --for=condition=Ready pods --all --timeout=3600s; do sleep 2; done
kubectl get svc -A -o wide
kubectl get pod -A -o wide
kubectl get all -o wide
@@ -89,7 +87,7 @@ jobs:
run: make ut
macos:
runs-on: macos-13
runs-on: macos-15-intel
needs: [ "image" ]
steps:
- uses: actions/checkout@v4
@@ -100,10 +98,6 @@ jobs:
go-version: '1.23'
check-latest: true
# https://github.com/crazy-max/ghaction-setup-docker/issues/108
- name: Set up QEMU
uses: docker/actions-toolkit/.github/actions/macos-setup-qemu@19ca9ade20f5da695f76a10988d6532058575f82
- name: Set up Docker
uses: docker/setup-docker-action@v4
with:
@@ -115,18 +109,18 @@ jobs:
}
}
- uses: azure/setup-kubectl@v4
- name: Install kind
run: |
set -x
docker version
brew install kind
kind create cluster
kubectl cluster-info
kubectl config view --flatten --raw
kubectl get pod -A -o wide
- name: Install minikube
timeout-minutes: 30
uses: medyagh/setup-minikube@latest
with:
cache: true
cpus: 'max'
memory: 'max'
- name: Kubernetes info
run: |
kubectl config view --flatten --raw
kubectl get pod -A -o wide
kubectl cluster-info
cat ~/.kube/config
kubectl get pods -n kube-system -o wide
@@ -148,9 +142,7 @@ jobs:
- name: Wait for pods reviews to be ready
run: |
kubectl wait pods -l app=reviews --for=condition=Ready --timeout=3600s
kubectl wait pods -l app=productpage --for=condition=Ready --timeout=3600s
kubectl wait pods -l app=authors --for=condition=Ready --timeout=3600s
while ! kubectl wait --for=condition=Ready pods --all --timeout=3600s; do sleep 2; done
kubectl get svc -A -o wide || true
kubectl get pod -A -o wide || true
kubectl get all -o wide || true
@@ -159,6 +151,9 @@ jobs:
netstat -anr
- name: Test
# for docker mount
env:
TMPDIR: .
run: make ut
windows:

View File

@@ -21,8 +21,7 @@ IMAGE_GH ?= ghcr.io/kubenetworks/kubevpn:$(VERSION)
IMAGE_GH_LATEST ?= ghcr.io/kubenetworks/kubevpn:latest
# Setup the -ldflags option for go build here, interpolate the variable values
# add '-tag noassets' for syncthing gui
LDFLAGS=-tags noassets --ldflags "-s -w\
LDFLAGS=--ldflags "-s -w\
-X ${BASE}/pkg/config.Image=${IMAGE_GH} \
-X ${BASE}/pkg/config.Version=${VERSION} \
-X ${BASE}/pkg/config.GitCommit=${GIT_COMMIT} \
@@ -96,7 +95,7 @@ container-local: kubevpn-linux-amd64
.PHONY: container-test
container-test: kubevpn-linux-amd64
docker buildx build --platform linux/amd64,linux/arm64 -t ${IMAGE} -t ${IMAGE_GH} -f $(BUILD_DIR)/test.Dockerfile --push .
docker build -t ${IMAGE_GH} -f $(BUILD_DIR)/test.Dockerfile --push .
.PHONY: version
version:
@@ -108,7 +107,7 @@ gen:
.PHONY: ut
ut:
go test -tags=noassets -coverprofile=coverage.txt -coverpkg=./... -test.v ./... -timeout=60m
go test -p=1 -v -timeout=60m -coverprofile=coverage.txt -coverpkg=./... ./...
.PHONY: cover
cover: ut

View File

@@ -56,7 +56,6 @@ With KubeVPN, empower yourself to develop applications entirely on your local PC
curl -fsSL https://kubevpn.dev/install.sh | sh
```
### Install from [brew](https://brew.sh/) (macOS / Linux)
```shell
@@ -138,9 +137,7 @@ Forwarding port...
Connected tunnel
Adding route...
Configured DNS service
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
@@ -148,8 +145,8 @@ already connected to cluster network, use command `kubevpn status` to check stat
```shell
➜ ~ kubevpn status
ID Mode Cluster Kubeconfig Namespace Status
0 full ccijorbccotmqodvr189g /Users/naison/.kube/config default Connected
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
➜ ~
```
@@ -189,7 +186,7 @@ NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
authors ClusterIP 172.21.5.160 <none> 9080/TCP 114d app=authors
details ClusterIP 172.21.6.183 <none> 9080/TCP 114d app=details
kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 319d <none>
kubevpn-traffic-manager ClusterIP 172.21.2.86 <none> 8422/UDP,10800/TCP,9002/TCP,80/TCP 2m28s app=kubevpn-traffic-manager
kubevpn-traffic-manager ClusterIP 172.21.2.86 <none> 10801/TCP,9002/TCP,80/TCP 2m28s app=kubevpn-traffic-manager
productpage ClusterIP 172.21.10.49 <none> 9080/TCP 114d app=productpage
ratings ClusterIP 172.21.3.247 <none> 9080/TCP 114d app=ratings
reviews ClusterIP 172.21.8.24 <none> 9080/TCP 114d app=reviews
@@ -252,21 +249,18 @@ use [Domain resolve](./README.md#domain-resolve)
### Connect to multiple kubernetes cluster network
- Mode `lite`: can connect to multiple cluster network, design for only connecting to multiple cluster network.
- Mode `Full`: not only connect to cluster network, it also supports proxy workloads inbound traffic to local PC.
already connected cluster `ccijorbccotmqodvr189g` with mode `full`
already connected cluster `ccijorbccotmqodvr189g`
```shell
➜ ~ kubevpn status
ID Mode Cluster Kubeconfig Namespace Status
0 full ccijorbccotmqodvr189g /Users/naison/.kube/config default Connected
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
```
then connect to another cluster `ccidd77aam2dtnc3qnddg` with mode `lite`
then connect to another cluster `ccidd77aam2dtnc3qnddg`
```shell
➜ ~ kubevpn connect -n default --kubeconfig ~/.kube/dev_config --lite
➜ ~ kubevpn connect -n default --kubeconfig ~/.kube/dev_config
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -274,18 +268,16 @@ Forwarding port...
Connected tunnel
Adding route...
Configured DNS service
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
```
use command `kubevpn status` to check connection status
```shell
➜ ~ kubevpn status
ID Mode Cluster Kubeconfig Namespace Status
0 full ccijorbccotmqodvr189g /Users/naison/.kube/config default Connected
1 lite ccidd77aam2dtnc3qnddg /Users/naison/.kube/dev_config default Connected
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
* 86bfdef0ed05 ccidd77aam2dtnc3qnddg /Users/naison/.kube/dev_config default connected utun5
➜ ~
```
@@ -301,9 +293,19 @@ Checking rollout status for deployment/productpage
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Rollout successfully for deployment/productpage
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
show status
```shell
➜ ~ kubevpn status
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
CONNECTION ID NAMESPACE NAME HEADERS PORTS CURRENT PC
03dc50feb8c3 default deployments.apps/productpage * 9080->9080 true
➜ ~
```
@@ -390,9 +392,19 @@ Checking rollout status for deployment/productpage
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Rollout successfully for deployment/productpage
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
show status
```shell
➜ ~ kubevpn status
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
CONNECTION ID NAMESPACE NAME HEADERS PORTS CURRENT PC
03dc50feb8c3 default deployments.apps/productpage foo=bar 9080->9080 true
➜ ~
```
@@ -430,13 +442,13 @@ Waiting for deployment "productpage" rollout to finish: 1 old replicas are pendi
Rollout successfully for deployments/productpage
```
### Dev mode in local Docker 🐳
### Run mode in local Docker 🐳
Run the Kubernetes pod in the local Docker container, and cooperate with the service mesh to intercept the traffic with
the specified header to the local, or all the traffic to the local.
```shell
➜ ~ kubevpn dev deployment/authors --headers foo=bar --entrypoint sh
➜ ~ kubevpn run deployment/authors --headers foo=bar --entrypoint sh
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -531,13 +543,13 @@ docker logs $(docker ps --format '{{.Names}}' | grep nginx_default_kubevpn)
If you just want to start up a docker image, you can use a simple way like this:
```shell
kubevpn dev deployment/authors --no-proxy
kubevpn run deployment/authors --no-proxy
```
Example
```shell
➜ ~ kubevpn dev deployment/authors --no-proxy
➜ ~ kubevpn run deployment/authors --no-proxy
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -565,7 +577,7 @@ Now the main process will hang up to show you log.
If you want to specify the image to start the container locally, you can use the parameter `--dev-image`. When the
image does not exist locally, it will be pulled from the corresponding mirror warehouse. If you want to specify startup
parameters, you can use `--entrypoint` parameter, replace it with the command you want to execute, such
as `--entrypoint /bin/bash`, for more parameters, see `kubevpn dev --help`.
as `--entrypoint /bin/bash`, for more parameters, see `kubevpn run --help`.
### DinD ( Docker in Docker ) use kubevpn in Docker
@@ -596,7 +608,7 @@ ca82aef6a9eb: Pull complete
Digest: sha256:368db2e0d98f6866dcefd60512960ce1310e85c24a398fea2a347905ced9507d
Status: Downloaded newer image for ghcr.io/kubenetworks/kubevpn:latest
WARNING: image with reference ghcr.io/kubenetworks/kubevpn was found but does not match the specified platform: wanted linux/amd64, actual: linux/arm64
root@5732124e6447:/app# kubevpn dev deployment/authors --headers user=naison --entrypoint sh
root@5732124e6447:/app# kubevpn run deployment/authors --headers user=naison --entrypoint sh
hostname is 5732124e6447
Starting connect
Got network CIDR from cache
@@ -625,7 +637,7 @@ Created main container: authors_default_kubevpn_6df5f
/opt/microservices # ps -ef
PID USER TIME COMMAND
1 root 0:00 {bash} /usr/bin/qemu-x86_64 /bin/bash /bin/bash
14 root 0:02 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn kubevpn dev deployment/authors --headers
14 root 0:02 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn kubevpn run deployment/authors --headers
25 root 0:01 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn /usr/local/bin/kubevpn daemon
37 root 0:04 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn /usr/local/bin/kubevpn daemon --sudo
53 root 0:00 nginx: master process nginx -g daemon off;
@@ -742,4 +754,6 @@ If you want to debug this project on local PC. Please follow the steps bellow:
### Supported by
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport)
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport)
### [Donate](https://kubevpn.dev/docs/donate)

View File

@@ -125,14 +125,19 @@ Forwarding port...
Connected tunnel
Adding route...
Configured DNS service
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
提示已经链接到集群了。使用命令 `kubevpn status` 检查一下状态。
```shell
➜ ~ kubevpn status
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
➜ ~
```
```shell
➜ ~ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
@@ -167,7 +172,7 @@ NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
authors ClusterIP 172.21.5.160 <none> 9080/TCP 114d app=authors
details ClusterIP 172.21.6.183 <none> 9080/TCP 114d app=details
kubernetes ClusterIP 172.21.0.1 <none> 443/TCP 319d <none>
kubevpn-traffic-manager ClusterIP 172.21.2.86 <none> 8422/UDP,10800/TCP,9002/TCP,80/TCP 2m28s app=kubevpn-traffic-manager
kubevpn-traffic-manager ClusterIP 172.21.2.86 <none> 10800/TCP,9002/TCP,80/TCP 2m28s app=kubevpn-traffic-manager
productpage ClusterIP 172.21.10.49 <none> 9080/TCP 114d app=productpage
ratings ClusterIP 172.21.3.247 <none> 9080/TCP 114d app=ratings
reviews ClusterIP 172.21.8.24 <none> 9080/TCP 114d app=reviews
@@ -231,23 +236,16 @@ reviews ClusterIP 172.21.8.24 <none> 9080/TCP
### 链接到多集群网络
有个两个模式
- 模式 `lite`: 可以链接到多个集群网络,但是仅支持链接到多集群。
- 模式 `full`: 不仅支持链接到单个集群网络,还可以拦截工作负载流量到本地电脑。
可以看到已经链接到了一个集群 `ccijorbccotmqodvr189g`,是 `full` 模式
可以看到已经链接到了一个集群 `ccijorbccotmqodvr189g`
```shell
➜ ~ kubevpn status
ID Mode Cluster Kubeconfig Namespace Status
0 full ccijorbccotmqodvr189g /Users/naison/.kube/config default Connected
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
```
此时还可以使用 `lite` 模式链接到其它集群
```shell
➜ ~ kubevpn connect -n default --kubeconfig ~/.kube/dev_config --lite
➜ ~ kubevpn connect -n default --kubeconfig ~/.kube/dev_config
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -255,18 +253,16 @@ Forwarding port...
Connected tunnel
Adding route...
Configured DNS service
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
```
使用命令 `kubevpn status` 查看当前链接状态。
```shell
➜ ~ kubevpn status
ID Mode Cluster Kubeconfig Namespace Status
0 full ccijorbccotmqodvr189g /Users/naison/.kube/config default Connected
1 lite ccidd77aam2dtnc3qnddg /Users/naison/.kube/dev_config default Connected
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
* 86bfdef0ed05 ccidd77aam2dtnc3qnddg /Users/naison/.kube/dev_config default connected utun5
➜ ~
```
@@ -284,9 +280,19 @@ Checking rollout status for deployment/productpage
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Rollout successfully for deployment/productpage
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
查看一下状态
```shell
➜ ~ kubevpn status
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
CONNECTION ID NAMESPACE NAME HEADERS PORTS CURRENT PC
03dc50feb8c3 default deployments.apps/productpage * 9080->9080 true
➜ ~
```
@@ -331,9 +337,19 @@ Checking rollout status for deployment/productpage
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "productpage" rollout to finish: 1 old replicas are pending termination...
Rollout successfully for deployment/productpage
+----------------------------------------------------------+
| Now you can access resources in the kubernetes cluster ! |
+----------------------------------------------------------+
Now you can access resources in the kubernetes cluster !
➜ ~
```
查询状态
```shell
➜ ~ kubevpn status
CURRENT CONNECTION ID CLUSTER KUBECONFIG NAMESPACE STATUS NETIF
* 03dc50feb8c3 ccijorbccotmqodvr189g /Users/naison/.kube/config default connected utun4
CONNECTION ID NAMESPACE NAME HEADERS PORTS CURRENT PC
03dc50feb8c3 default deployments.apps/productpage foo=bar 9080->9080 true
➜ ~
```
@@ -370,13 +386,13 @@ Waiting for deployment "productpage" rollout to finish: 1 old replicas are pendi
Rollout successfully for deployments/productpage
```
### 本地进入开发模式 🐳
### 本地进入运行模式 🐳
将 Kubernetes pod 运行在本地的 Docker 容器中,同时配合 service mesh, 拦截带有指定 header 的流量到本地,或者所有的流量到本地。这个开发模式依赖于本地
Docker。
```shell
➜ ~ kubevpn dev deployment/authors --headers foo=bar --entrypoint sh
➜ ~ kubevpn run deployment/authors --headers foo=bar --entrypoint sh
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -456,13 +472,13 @@ fc04e42799a5 nginx:latest "/docker-entrypoint.…" 37 sec
如果你只是想在本地启动镜像,可以用一种简单的方式:
```shell
kubevpn dev deployment/authors --no-proxy
kubevpn run deployment/authors --no-proxy
```
例如:
```shell
➜ ~ kubevpn dev deployment/authors --no-proxy
➜ ~ kubevpn run deployment/authors --no-proxy
Starting connect
Got network CIDR from cache
Use exist traffic manager
@@ -489,7 +505,7 @@ Created main container: authors_default_kubevpn_ff34b
如果你想指定在本地启动容器的镜像, 可以使用参数 `--dev-image`, 当本地不存在该镜像时,
会从对应的镜像仓库拉取。如果你想指定启动参数,可以使用 `--entrypoint`
参数,替换为你想要执行的命令,比如 `--entrypoint /bin/bash`, 更多使用参数,请参见 `kubevpn dev --help`.
参数,替换为你想要执行的命令,比如 `--entrypoint /bin/bash`, 更多使用参数,请参见 `kubevpn run --help`.
### DinD ( Docker in Docker ) 在 Docker 中使用 kubevpn
@@ -519,7 +535,7 @@ ca82aef6a9eb: Pull complete
Digest: sha256:368db2e0d98f6866dcefd60512960ce1310e85c24a398fea2a347905ced9507d
Status: Downloaded newer image for ghcr.io/kubenetworks/kubevpn:latest
WARNING: image with reference ghcr.io/kubenetworks/kubevpn was found but does not match the specified platform: wanted linux/amd64, actual: linux/arm64
root@5732124e6447:/app# kubevpn dev deployment/authors --headers user=naison --entrypoint sh
root@5732124e6447:/app# kubevpn run deployment/authors --headers user=naison --entrypoint sh
hostname is 5732124e6447
Starting connect
Got network CIDR from cache
@@ -548,7 +564,7 @@ Created main container: authors_default_kubevpn_6df5f
/opt/microservices # ps -ef
PID USER TIME COMMAND
1 root 0:00 {bash} /usr/bin/qemu-x86_64 /bin/bash /bin/bash
14 root 0:02 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn kubevpn dev deployment/authors --headers
14 root 0:02 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn kubevpn run deployment/authors --headers
25 root 0:01 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn /usr/local/bin/kubevpn daemon
37 root 0:04 {kubevpn} /usr/bin/qemu-x86_64 /usr/local/bin/kubevpn /usr/local/bin/kubevpn daemon --sudo
53 root 0:00 nginx: master process nginx -g daemon off;
@@ -660,3 +676,5 @@ d0b3dab8912a ghcr.io/kubenetworks/kubevpn:v2.0.0 "/bin/bash" 5
### 支持者
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport)
### [捐赠支持](https://kubevpn.dev/zh/docs/donate/)

View File

@@ -1,4 +1,4 @@
FROM envoyproxy/envoy:v1.25.0 AS envoy
FROM envoyproxy/envoy:v1.36.2 AS envoy
FROM golang:1.23 AS builder
ARG BASE=github.com/wencaiwulue/kubevpn
@@ -6,40 +6,17 @@ COPY . /go/src/$BASE
WORKDIR /go/src/$BASE
RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct
RUN make kubevpn
RUN go install github.com/go-delve/delve/cmd/dlv@latest
FROM ubuntu:latest
FROM debian:bookworm-slim
ARG BASE=github.com/wencaiwulue/kubevpn
RUN sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
RUN apt-get clean && apt-get update && apt-get install -y wget dnsutils vim curl \
net-tools iptables iputils-ping lsof iproute2 tcpdump binutils traceroute conntrack socat iperf3 \
apt-transport-https ca-certificates curl
RUN if [ $(uname -m) = "x86_64" ]; then \
echo "The architecture is AMD64"; \
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x kubectl && mv kubectl /usr/local/bin; \
elif [ $(uname -m) = "aarch64" ]; then \
echo "The architecture is ARM64"; \
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl" && chmod +x kubectl && mv kubectl /usr/local/bin; \
else \
echo "Unsupported architecture."; \
fi
ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive
RUN apt update \
&& apt install -y tzdata \
&& ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& dpkg-reconfigure --frontend noninteractive tzdata \
RUN apt-get update && apt-get install -y iptables dnsutils \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /go/src/$BASE/bin/kubevpn /usr/local/bin/kubevpn
COPY --from=builder /go/bin/dlv /usr/local/bin/dlv
COPY --from=envoy /usr/local/bin/envoy /usr/local/bin/envoy

View File

@@ -1,6 +1,6 @@
FROM golang:1.23 as delve
RUN curl --location --output delve-1.23.1.tar.gz https://github.com/go-delve/delve/archive/v1.23.1.tar.gz \
&& tar xzf delve-1.23.1.tar.gz
RUN cd delve-1.23.1 && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /go/dlv -ldflags '-extldflags "-static"' ./cmd/dlv/
FROM golang:1.25 as delve
RUN curl --location --output delve-1.25.2.tar.gz https://github.com/go-delve/delve/archive/v1.25.2.tar.gz \
&& tar xzf delve-1.25.2.tar.gz
RUN cd delve-1.25.2 && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /go/dlv -ldflags '-extldflags "-static"' ./cmd/dlv/
FROM busybox
COPY --from=delve /go/dlv /bin/dlv

View File

@@ -2,7 +2,7 @@ FROM golang:1.23 AS builder
RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct
RUN go install github.com/go-delve/delve/cmd/dlv@latest
FROM envoyproxy/envoy:v1.25.0 AS envoy
FROM envoyproxy/envoy:v1.36.2 AS envoy
FROM ubuntu:latest
RUN sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
@@ -11,16 +11,6 @@ RUN apt-get clean && apt-get update && apt-get install -y wget dnsutils vim curl
net-tools iptables iputils-ping lsof iproute2 tcpdump binutils traceroute conntrack socat iperf3 \
apt-transport-https ca-certificates curl
RUN if [ $(uname -m) = "x86_64" ]; then \
echo "The architecture is AMD64"; \
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && chmod +x kubectl && mv kubectl /usr/local/bin; \
elif [ $(uname -m) = "aarch64" ]; then \
echo "The architecture is ARM64"; \
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl" && chmod +x kubectl && mv kubectl /usr/local/bin; \
else \
echo "Unsupported architecture."; \
fi
ENV TZ=Asia/Shanghai \
DEBIAN_FRONTEND=noninteractive
RUN apt update \

View File

@@ -1,5 +1,22 @@
FROM ghcr.io/kubenetworks/kubevpn:latest
FROM envoyproxy/envoy:v1.36.2 AS envoy
FROM golang:1.23 AS builder
ARG BASE=github.com/wencaiwulue/kubevpn
COPY . /go/src/$BASE
WORKDIR /go/src/$BASE
RUN make kubevpn
FROM debian:bookworm-slim
ARG BASE=github.com/wencaiwulue/kubevpn
RUN apt-get update && apt-get install -y iptables dnsutils \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY bin/kubevpn /usr/local/bin/kubevpn
COPY --from=builder /go/src/$BASE/bin/kubevpn /usr/local/bin/kubevpn
COPY --from=envoy /usr/local/bin/envoy /usr/local/bin/envoy

View File

@@ -1,6 +1,496 @@
apiVersion: v1
entries:
kubevpn:
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.11
created: "2025-12-05T07:14:17.608868476Z"
description: A Helm chart for KubeVPN
digest: 3c3ca6c48b01b81d873ea154f61fbfad9a07e7dd42e8281d824b1f2f399b311a
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.11/kubevpn-2.9.11.tgz
version: 2.9.11
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.10
created: "2025-10-22T03:51:29.629069653Z"
description: A Helm chart for KubeVPN
digest: 6704a23053784668cb639788f5ccf01b61ee9f59038320371a0178a191a1fc32
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.10/kubevpn-2.9.10.tgz
version: 2.9.10
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.9
created: "2025-09-30T09:22:55.555652388Z"
description: A Helm chart for KubeVPN
digest: c8568e43813bc79266da5a46e7bc3d555f64ffebfef38b3b46551f524873b0d4
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.9/kubevpn-2.9.9.tgz
version: 2.9.9
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.8
created: "2025-09-25T13:53:09.954190561Z"
description: A Helm chart for KubeVPN
digest: 88ee21f2ae63109d1e91ea95275e4811e825c2940e03decf5d4da209e6f811ad
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.8/kubevpn-2.9.8.tgz
version: 2.9.8
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.7
created: "2025-08-28T13:09:32.61187108Z"
description: A Helm chart for KubeVPN
digest: 69db43fa21c8d23aa006d4473cc7250682533bf79b9c9f31c8169162e7a30061
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.7/kubevpn-2.9.7.tgz
version: 2.9.7
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.6
created: "2025-08-21T15:24:04.135714125Z"
description: A Helm chart for KubeVPN
digest: 329ffe609c96df844facd678fb07a56e18000d19118f61f34b43f75bf6a6fbe3
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.6/kubevpn-2.9.6.tgz
version: 2.9.6
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.5
created: "2025-08-10T12:41:13.574529804Z"
description: A Helm chart for KubeVPN
digest: e92b796337f4bbbad7be76ee9b8f2c58fedcc43f095846245dc9e2a136619685
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.5/kubevpn-2.9.5.tgz
version: 2.9.5
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.4
created: "2025-08-10T07:02:14.35432727Z"
description: A Helm chart for KubeVPN
digest: 331ae4c250ba070f1928b2dd248631fb5e842e576f83fe85c4591186fecaf84e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.4/kubevpn-2.9.4.tgz
version: 2.9.4
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.3
created: "2025-08-06T16:24:32.06997396Z"
description: A Helm chart for KubeVPN
digest: 1e0e611ac03a86ed07e5766747902b5f495e8365136bf38d63abf513f5dce18e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.3/kubevpn-2.9.3.tgz
version: 2.9.3
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.2
created: "2025-08-06T11:30:00.252464538Z"
description: A Helm chart for KubeVPN
digest: dbdda813051ab2bb37c93ea58c3722c11730ebd66dc68fc9b9fb6f2c9e5fa3a6
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.2/kubevpn-2.9.2.tgz
version: 2.9.2
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.1
created: "2025-08-04T15:38:16.369392863Z"
description: A Helm chart for KubeVPN
digest: 16834555367165a0411bafbf15d63d50f28434ad4aada8a9cf5622cc51fd6a30
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.1/kubevpn-2.9.1.tgz
version: 2.9.1
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.9.0
created: "2025-07-27T13:58:23.265991449Z"
description: A Helm chart for KubeVPN
digest: a7f7b8dd5a05c1f251d01cdf0bb697d98904b41d3a24ae569966c41a7a576701
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.9.0/kubevpn-2.9.0.tgz
version: 2.9.0
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.8.1
created: "2025-07-10T03:19:24.597366484Z"
description: A Helm chart for KubeVPN
digest: 94c8af0d1731b8936c60c7334f1b38dd760fa40b383094a46dc47b20da7b6154
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.8.1/kubevpn-2.8.1.tgz
version: 2.8.1
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.8.0
created: "2025-07-05T14:28:46.57918859Z"
description: A Helm chart for KubeVPN
digest: e1cd48c247366a3751b35b10baaed4d81891cc6d4a7b290468356f14fe9b22c6
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.8.0/kubevpn-2.8.0.tgz
version: 2.8.0
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.21
created: "2025-07-04T12:51:41.427883149Z"
description: A Helm chart for KubeVPN
digest: 3f98084d53d9c49ae3acfcaecb5cca8f75629a17172c0ec83404067b9c2537af
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.21/kubevpn-2.7.21.tgz
version: 2.7.21
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.20
created: "2025-06-26T04:09:53.467005029Z"
description: A Helm chart for KubeVPN
digest: 692beb037244d85f730b8bccffcc7fdbfc421b2e8fc7965fa8db0574a674539b
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.20/kubevpn-2.7.20.tgz
version: 2.7.20
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.19
created: "2025-06-18T10:22:35.562802687Z"
description: A Helm chart for KubeVPN
digest: df20091ef30e5666b5e859a91fa95fe88e195b3bf822154dbf2b9773ffca767c
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.19/kubevpn-2.7.19.tgz
version: 2.7.19
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.18
created: "2025-06-14T06:12:56.525323112Z"
description: A Helm chart for KubeVPN
digest: 5e58ac6222a9de298719246e644d2c95098d55ee479e5415153fa460b2562960
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.18/kubevpn-2.7.18.tgz
version: 2.7.18
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.17
created: "2025-06-12T06:01:29.967945392Z"
description: A Helm chart for KubeVPN
digest: 2b680300b9d203fe2df91fe19876908477e9216569065e54d6746d88fb9c6014
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.17/kubevpn-2.7.17.tgz
version: 2.7.17
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.16
created: "2025-06-10T15:10:07.72781521Z"
description: A Helm chart for KubeVPN
digest: cf5f4af9e33ba051a3cdb861f9c74ff8b8552c94fd64d13143ada7cbbeb8681d
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.16/kubevpn-2.7.16.tgz
version: 2.7.16
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.15
created: "2025-06-07T05:29:43.999105856Z"
description: A Helm chart for KubeVPN
digest: 030abfc966892a0ee644ceeb641e39d098eb94447f87cb1b59eba11fcd12f783
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.15/kubevpn-2.7.15.tgz
version: 2.7.15
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.14
created: "2025-06-04T07:14:42.336616618Z"
description: A Helm chart for KubeVPN
digest: dd1d35c9a7cb6411793cbf00a784aa0ded32de9354f0db3559014e339c6985d4
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.14/kubevpn-2.7.14.tgz
version: 2.7.14
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.13
created: "2025-06-01T11:26:39.769366159Z"
description: A Helm chart for KubeVPN
digest: 167b52f7f59b0a6c01c2660d1a0a9a71c315eeeabc0cf9ff1047ef5dfd4a06e6
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.13/kubevpn-2.7.13.tgz
version: 2.7.13
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.12
created: "2025-05-23T03:38:02.484001975Z"
description: A Helm chart for KubeVPN
digest: b9e28ceda8bb07b42ec37eb2d6b283496d83645479b2f1f4e921d9c462eeb54e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.12/kubevpn-2.7.12.tgz
version: 2.7.12
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.11
created: "2025-05-18T09:03:21.60777933Z"
description: A Helm chart for KubeVPN
digest: ee30c2533dff51fa389767e56931583cdfff8c5fca7d6c9698f521c6fc508d42
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.11/kubevpn-2.7.11.tgz
version: 2.7.11
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.10
created: "2025-05-14T13:08:51.09371872Z"
description: A Helm chart for KubeVPN
digest: fd23dd5bf0c3a9343d73276c4997a34027a93c1a88667265d92297630579d165
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.10/kubevpn-2.7.10.tgz
version: 2.7.10
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.9
created: "2025-05-12T09:14:52.66116293Z"
description: A Helm chart for KubeVPN
digest: 56e022017177603290575849553c2e9c19f6a1691288dbd67c32a2fdcbde0834
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.9/kubevpn-2.7.9.tgz
version: 2.7.9
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.8
created: "2025-05-10T15:46:13.342045201Z"
description: A Helm chart for KubeVPN
digest: bfab5a7e4e1e795071a7ce3fd7713b517aa447d967ec58500e5a551564869109
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.8/kubevpn-2.7.8.tgz
version: 2.7.8
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.7
created: "2025-05-09T06:43:01.403047355Z"
description: A Helm chart for KubeVPN
digest: 14b3e7873aa71fa7a380631c83be8df1dfb8d0ccb49eb6746aa4f83e3df934f6
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.7/kubevpn-2.7.7.tgz
version: 2.7.7
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.6
created: "2025-05-07T11:46:09.644201893Z"
description: A Helm chart for KubeVPN
digest: 2146d5245440dff7d551ccc745aa1d9476d4f42053ff8a80f33f835d8da57712
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.6/kubevpn-2.7.6.tgz
version: 2.7.6
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.5
created: "2025-05-07T01:56:15.201307242Z"
description: A Helm chart for KubeVPN
digest: 34799e9605b3048aac75484bb32fb6c70f9e7eb7470e9b77c51be075a548c25e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.5/kubevpn-2.7.5.tgz
version: 2.7.5
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.4
created: "2025-05-06T17:01:13.789138284Z"
description: A Helm chart for KubeVPN
digest: 5c6f2d1a178e917ac83ec72d0a46de9a0ff68f80a3aeb813d15dfb92c8ad36be
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.4/kubevpn-2.7.4.tgz
version: 2.7.4
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.3
created: "2025-05-06T15:40:24.505449375Z"
description: A Helm chart for KubeVPN
digest: 86ef4b1de6ea15f6738824f7c389a891f53500b9163b1288847172eb7dc6817e
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.3/kubevpn-2.7.3.tgz
version: 2.7.3
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.2
created: "2025-04-25T15:40:08.296727519Z"
description: A Helm chart for KubeVPN
digest: 8711dae30f4ff9bc9cea018fa16ae70087a17af42262f7f31c43950a34fffa08
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.2/kubevpn-2.7.2.tgz
version: 2.7.2
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.1
created: "2025-04-15T15:18:20.818055207Z"
description: A Helm chart for KubeVPN
digest: 79c40c942fd2cfcca63dd82921e04871680838f01717c6fcb3ee06bfb7f59535
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.1/kubevpn-2.7.1.tgz
version: 2.7.1
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.7.0
created: "2025-04-12T05:37:01.063235951Z"
description: A Helm chart for KubeVPN
digest: a4b4de15f474fba43367fc7239c31e2020a6a1e0e3b29e02eb653cb9922b02e8
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.7.0/kubevpn-2.7.0.tgz
version: 2.7.0
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.6.0
created: "2025-04-06T12:54:49.852649414Z"
description: A Helm chart for KubeVPN
digest: 58d930de19ac808e9f0ee501fe6f74b6f38376692708fc94fe7200496d9c5ca2
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.6.0/kubevpn-2.6.0.tgz
version: 2.6.0
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.5.1
created: "2025-04-03T15:46:28.062220333Z"
description: A Helm chart for KubeVPN
digest: 6daf003256c42bb0db414eb17eb06294e46d33bc6c63f01419012a37318d0d2f
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.5.1/kubevpn-2.5.1.tgz
version: 2.5.1
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.5.0
created: "2025-03-31T05:36:16.050204161Z"
description: A Helm chart for KubeVPN
digest: 301137b1599c232efd61ce9360e0a60da89e0a5c2eb076750bf461b38d26cfaf
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.5.0/kubevpn-2.5.0.tgz
version: 2.5.0
- annotations:
app: kubevpn
apiVersion: v2
appVersion: v2.4.3
created: "2025-03-30T13:48:42.333380676Z"
description: A Helm chart for KubeVPN
digest: 8ef28a43cb3d04f071445cf7d1199aba7392d78e1941707bab82853c5541c93c
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.4.3/kubevpn-2.4.3.tgz
version: 2.4.3
- apiVersion: v2
appVersion: v2.4.2
created: "2025-03-23T12:53:35.793492243Z"
description: A Helm chart for KubeVPN
digest: c627f69ac904ddb41c396909873425d85264fb3393d550fa1b0e8d2abfc402e9
name: kubevpn
type: application
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.4.2/kubevpn-2.4.2.tgz
version: 2.4.2
- apiVersion: v2
appVersion: v2.4.1
created: "2025-03-16T09:48:30.691242519Z"
@@ -361,4 +851,4 @@ entries:
urls:
- https://github.com/kubenetworks/kubevpn/releases/download/v2.2.2/kubevpn-2.2.2.tgz
version: 2.2.2
generated: "2025-03-16T09:48:30.691536105Z"
generated: "2025-12-05T07:14:17.609092756Z"

View File

@@ -4,3 +4,5 @@ description: A Helm chart for KubeVPN
type: application
version: 0.1.0
appVersion: "1.16.0"
annotations:
app: kubevpn

36
charts/kubevpn/README.md Normal file
View File

@@ -0,0 +1,36 @@
# Helm charts for KubeVPN server
Use helm to install kubevpn server means use cluster mode. All user will use this instance.
- Please make sure users should have permission to namespace `kubevpn`.
- Otherwise, will fall back to create `kubevpn` deployment in own namespace.
## Add helm repository kubevpn
```shell
helm repo add kubevpn https://kubenetworks.github.io/charts
```
## Install with default mode
```shell
helm install kubevpn kubevpn/kubevpn -n kubevpn --create-namespace
```
in China, you can use tencent image registry
```shell
helm install kubevpn kubevpn/kubevpn --set image.repository=ccr.ccs.tencentyun.com/kubevpn/kubevpn -n kubevpn --create-namespace
```
## AWS Fargate cluster
```shell
helm install kubevpn kubevpn/kubevpn -n kubevpn --create-namespace
```
*Proxy/ServiceMesh mode only support k8s service*
```shell
kubevpn proxy service/authors
```

View File

@@ -1,4 +1,4 @@
1. Connect to cluster network by running these commands:
kubevpn connect --namespace {{ .Release.Namespace }}
export POD_IP=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "kubevpn.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].status.podIP}")
kubevpn connect --namespace {{ include "kubevpn.namespace" . }}
export POD_IP=$(kubectl get pods --namespace {{ include "kubevpn.namespace" . }} -l "app.kubernetes.io/name={{ include "kubevpn.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].status.podIP}")
ping $POD_IP

View File

@@ -61,3 +61,22 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Namespace
1. special by -n
2. use default namespace kubevpn
*/}}
{{- define "kubevpn.namespace" -}}
{{- if .Release.Namespace }}
{{- if eq .Release.Namespace "default" }}
{{- .Values.namespace }}
{{- else }}
{{- .Release.Namespace }}
{{- end }}
{{- else if .Values.namespace }}
{{- .Values.namespace }}
{{- else }}
{{- .Values.namespace }}
{{- end }}
{{- end }}

View File

@@ -2,9 +2,17 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
data:
{{- $existingConfigmap := (lookup "v1" "ConfigMap" (include "kubevpn.namespace" .) (include "kubevpn.fullname" .)) }}
{{- if $existingConfigmap }}
{{- range $key, $value := $existingConfigmap.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- else }}
DHCP: ""
DHCP6: ""
ENVOY_CONFIG: ""
IPv4_POOLS: "{{ .Values.cidr.pod }} {{ .Values.cidr.service }}"
REF_COUNT: "0"
{{- end }}

View File

@@ -2,6 +2,7 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
labels:
{{- include "kubevpn.labels" . | nindent 4 }}
spec:
@@ -31,34 +32,12 @@ spec:
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- args:
- |2-
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.disable_ipv6=0
sysctl -w net.ipv6.conf.all.forwarding=1
update-alternatives --set iptables /usr/sbin/iptables-legacy
iptables -F
ip6tables -F
iptables -P INPUT ACCEPT
ip6tables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
ip6tables -P FORWARD ACCEPT
iptables -t nat -A POSTROUTING -s ${CIDR4} -o eth0 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -s ${CIDR6} -o eth0 -j MASQUERADE
kubevpn serve -L "tcp://:10800" -L "tun://:8422?net=${TunIPv4}" -L "gtcp://:10801" -L "gudp://:10802" --debug=true
command:
- /bin/sh
- -c
env:
- name: CIDR4
value: 198.19.0.0/16
- name: CIDR6
value: 2001:2::/64
- name: TunIPv4
value: 198.19.0.100/16
- name: TunIPv6
value: 2001:2::9999/64
- command:
- kubevpn
args:
- server
- -l gtcp://:10801
- -l gudp://:10802
envFrom:
- secretRef:
name: {{ include "kubevpn.fullname" . }}
@@ -66,24 +45,13 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: vpn
ports:
- containerPort: {{ .Values.service.port8422 }}
name: 8422-for-udp
protocol: UDP
- containerPort: {{ .Values.service.port10800 }}
name: 10800-for-tcp
- containerPort: {{ .Values.service.port10801 }}
name: 10801-for-tcp
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 12 }}
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
runAsUser: 0
- args:
- control-plane
- --watchDirectoryFilename
- /etc/envoy/envoy-config.yaml
command:
- kubevpn
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
@@ -95,10 +63,6 @@ spec:
protocol: TCP
resources:
{{- toYaml .Values.resourcesSmall | nindent 12 }}
volumeMounts:
- mountPath: /etc/envoy
name: envoy-config
readOnly: true
- args:
- webhook
command:
@@ -106,6 +70,11 @@ spec:
envFrom:
- secretRef:
name: {{ include "kubevpn.fullname" . }}
env:
- name: "POD_NAMESPACE"
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: webhook

View File

@@ -3,6 +3,7 @@ apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
labels:
{{- include "kubevpn.labels" . | nindent 4 }}
spec:

View File

@@ -2,6 +2,7 @@ apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
labels:
{{- include "kubevpn.labels" . | nindent 4 }}
annotations:
@@ -31,42 +32,7 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/bash
- -c
args:
- |2-
echo "Label namespace {{ .Release.Namespace }}"
kubectl label ns {{ .Release.Namespace }} ns={{ .Release.Namespace }}
echo "Generating https certificate"
openssl req -x509 -nodes -days 36500 -newkey rsa:2048 -subj "/CN={{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}.svc" -addext "subjectAltName=DNS:{{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local,DNS:{{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}.svc" -keyout server.key -out server.crt
export TLS_CRT=$(cat server.crt | base64 | tr -d '\n')
echo "Patch mutatingwebhookconfigurations {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}"
kubectl patch mutatingwebhookconfigurations {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }} -p "{\"webhooks\":[{\"name\":\"{{ include "kubevpn.fullname" . }}.naison.io\",\"sideEffects\":\"None\",\"admissionReviewVersions\":[\"v1\", \"v1beta1\"],\"clientConfig\":{\"service\":{\"namespace\":\"{{ .Release.Namespace }}\",\"name\":\"{{ include "kubevpn.fullname" . }}\"},\"caBundle\":\"$TLS_CRT\"}}]}"
export TLS_KEY=$(cat server.key | base64 | tr -d '\n')
echo "Patch secret {{ include "kubevpn.fullname" . }}"
kubectl patch secret {{ include "kubevpn.fullname" . }} -n {{ .Release.Namespace }} -p "{\"data\":{\"tls_key\":\"$TLS_KEY\",\"tls_crt\":\"$TLS_CRT\"}}"
echo "Restart the pods..."
kubectl scale -n {{ .Release.Namespace }} --replicas=0 deployment/{{ include "kubevpn.fullname" . }}
kubectl scale -n {{ .Release.Namespace }} --replicas=1 deployment/{{ include "kubevpn.fullname" . }}
export POOLS=$(kubectl get cm {{ include "kubevpn.fullname" . }} -n {{ .Release.Namespace }} -o jsonpath='{.data.IPv4_POOLS}')
if [[ -z "${POOLS// }" ]];then
echo "Cidr is empty"
echo "Get pod cidr..."
export POD_CIDR=$(kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}' | tr -s '\n' ' ')
echo "Get service cidr..."
export SVC_CIDR=$(echo '{"apiVersion":"v1","kind":"Service","metadata":{"name":"kubevpn-get-svc-cidr-{{ .Release.Namespace }}", "namespace": "{{ .Release.Namespace }}"},"spec":{"clusterIP":"1.1.1.1","ports":[{"port":443}]}}' | kubectl apply -f - 2>&1 | sed 's/.*valid IPs is //')
echo "Pod cidr: $POD_CIDR, service cidr: $SVC_CIDR"
echo "Patch configmap {{ include "kubevpn.fullname" . }}"
kubectl patch configmap {{ include "kubevpn.fullname" . }} -n {{ .Release.Namespace }} -p "{\"data\":{\"IPv4_POOLS\":\"$POD_CIDR $SVC_CIDR\"}}"
else
echo "Cidr is NOT empty"
fi
echo "Done~"
exit 0
- kubevpn
- once
- --image
- "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"

View File

@@ -1,24 +1,28 @@
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}
name: {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
namespace: {{ include "kubevpn.namespace" . }}
webhooks:
- admissionReviewVersions:
- v1
- v1beta1
clientConfig:
{{- $secret := (lookup "v1" "Secret" (include "kubevpn.namespace" .) (include "kubevpn.fullname" .)) }}
{{- if $secret }}
caBundle: {{ index $secret.data "tls_crt" }}
{{- else }}
caBundle: {{ .Values.tls.crt }}
{{- end }}
service:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ .Release.Namespace }}
namespace: {{ include "kubevpn.namespace" . }}
path: /pods
port: 80
failurePolicy: Ignore
matchPolicy: Equivalent
name: {{ include "kubevpn.fullname" . }}.naison.io
namespaceSelector:
matchLabels:
ns: {{ .Release.Namespace }}
namespaceSelector: { }
objectSelector: { }
reinvocationPolicy: Never
rules:

View File

@@ -2,6 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
rules:
- apiGroups:
- ""
@@ -20,10 +21,10 @@ rules:
- delete
- apiGroups: [ "" ]
resources: [ "namespaces" ]
resourceNames: [{{ .Release.Namespace }}]
resourceNames: [{{ include "kubevpn.namespace" . }}]
verbs:
- get
- patch
- update
- apiGroups: [ "apps" ]
resources: [ "deployments/scale", "deployments" ]
resourceNames:
@@ -42,23 +43,42 @@ rules:
- get
- update
- patch
- list
# for get network cidr
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create
- apiGroups:
- ""
resources:
- pods
- pods/log
verbs:
- list
- get
- create
- delete
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}
name: {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
rules:
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
resourceNames:
- {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}
- {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
verbs:
- get
- list
- patch
- update
- apiGroups:
- ""
resources:
@@ -66,4 +86,11 @@ rules:
verbs:
- get
- list
- watch
- watch
# for get network cidr
- apiGroups:
- ""
resources:
- pods
verbs:
- list

View File

@@ -2,6 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
@@ -9,18 +10,18 @@ roleRef:
subjects:
- kind: ServiceAccount
name: {{ include "kubevpn.fullname" . }}
namespace: {{ .Release.Namespace }}
namespace: {{ include "kubevpn.namespace" . }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}
name: {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
subjects:
- kind: ServiceAccount
name: {{ include "kubevpn.fullname" . }}
namespace: {{ .Release.Namespace }}
namespace: {{ include "kubevpn.namespace" . }}
roleRef:
kind: ClusterRole
name: {{ include "kubevpn.fullname" . }}.{{ .Release.Namespace }}
name: {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
apiGroup: rbac.authorization.k8s.io

View File

@@ -1,8 +1,19 @@
apiVersion: v1
data:
tls_crt: {{ .Values.tls.crt }}
tls_key: {{ .Values.tls.key }}
kind: Secret
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
type: Opaque
{{- $secret := (lookup "v1" "Secret" (include "kubevpn.namespace" .) (include "kubevpn.fullname" .)) }}
{{- if $secret }}
data:
{{- range $key, $value := $secret.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- else }}
stringData:
tls_crt: {{ .Values.tls.crt }}
tls_key: {{ .Values.tls.key }}
tls_server_name: {{ include "kubevpn.fullname" . }}.{{ include "kubevpn.namespace" . }}
{{- end }}

View File

@@ -2,19 +2,16 @@ apiVersion: v1
kind: Service
metadata:
name: {{ include "kubevpn.fullname" . }}
namespace: {{ include "kubevpn.namespace" . }}
labels:
{{- include "kubevpn.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- name: 8422-for-udp
port: {{ .Values.service.port8422 }}
protocol: UDP
targetPort: 8422
- name: 10800-for-tcp
port: {{ .Values.service.port10800 }}
- name: 10801-for-tcp
port: {{ .Values.service.port10801 }}
protocol: TCP
targetPort: 10800
targetPort: 10801
- name: 9002-for-envoy
port: {{ .Values.service.port9002 }}
protocol: TCP

View File

@@ -3,6 +3,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "kubevpn.serviceAccountName" . }}
namespace: {{ include "kubevpn.namespace" . }}
labels:
{{- include "kubevpn.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}

View File

@@ -2,6 +2,9 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# default namespace
namespace: kubevpn
replicaCount: 1
image:
@@ -21,8 +24,9 @@ cidr:
service: ""
tls:
crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURXVENDQWtHZ0F3SUJBZ0lJU0NmUDdHeHVhUkl3RFFZSktvWklodmNOQVFFTEJRQXdNREV1TUN3R0ExVUUKQXd3bGEzVmlaWFp3YmkxMGNtRm1abWxqTFcxaGJtRm5aWEl0WTJGQU1UY3dOamsyTnpjd01EQWVGdzB5TkRBeQpNRE14TWpReE5EQmFGdzB5TlRBeU1ESXhNalF4TkRCYU1DMHhLekFwQmdOVkJBTU1JbXQxWW1WMmNHNHRkSEpoClptWnBZeTF0WVc1aFoyVnlRREUzTURZNU5qYzNNREF3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXcKZ2dFS0FvSUJBUURzVnNleEVpVG00dmlleUhEeU5SbldKbXNiaFBWV24yTkgvNi9wUGVBT3ZUbXgwSDdHUnZJLwpzMzVoZW9EWExhdFVmaDlXT1hXdzRqaGZsdUdWQWlzZGs2Y2ZkS1hVVzJheXpRbFpZd1ZMTzdUUHFoeWF0UHVpCmpRYVB2bUErRGNYMHJRc2Y3SFJwVWhjVTJ1QTJ4WGhZNy9QWWFUdzhkU0NTTHFTK2ZLM3poc0lONTFrYnIzdG4KU2FKcWFybDNhSU82N1JvdmNZbmxERG9XTzFwS1ZSUmROVkM1anVtREJOSWdOam5TSTY5QTFydzR0REkwdjcxWQpPRmhjYnUwNnFVdkNNU1JzR3F5ZkhOeUlXakVvcnk4Wk0xVExlcnZhTk12WlFTRndRNk5SRExHYXNlbTBlNTRXCmVublA0OVpIR1FhTjllYnJQSkJuL2pQQ3p0NlFDMkg5QWdNQkFBR2plakI0TUE0R0ExVWREd0VCL3dRRUF3SUYKb0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQQpGQVA3WmhvcGsvbEc3MVNCMk42QkpKdDI2eXhuTUNJR0ExVWRFUVFiTUJtQ0YydDFZbVYyY0c0dGRISmhabVpwCll5MXRZVzVoWjJWeU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQVhYWk1WazhhQWwwZTlqUWRQTDc3ZVZOL3kKY1ZZZzRBVDlhdkh0UXV2UkZnOU80Z3JMaFVDQnoyN25wdlZZcHNMbmdEMTFRTXpYdHlsRDNMNDJNQ3V0Wnk5VQorL1BCL291ajQzWkZUckJDbk9DZDl6elE2MXZSL1RmbUFrTUhObTNZYjE1OGt2V0ZhNVlBdytRVi9vRDNUcGlXClREVTZXNkxvRFg5N0lNSFk0L3VLNTNzbXVLMjh5VzduSVVrbnpqN3h5UzVOWTFZaVNUN0w2ZFZ0VVppR1FUK00KRk16ODVRcTJOTWVXU1lKTmhhQVk5WEpwMXkrcEhoeWpPVFdjSEFNYmlPR29mODM5N1R6YmUyWHdNQ3BGMWc5NwpMaHZERnNsNzcyOWs1NFJVb1d2ZjFIVFFxL2R6cVBQTTNhWGpTbXFWUEV2Zk5qeGNhZnFnNHBaRmdzYzEKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJREpEQ0NBZ3lnQXdJQkFnSUlJMmROaFBKY0Uxc3dEUVlKS29aSWh2Y05BUUVMQlFBd01ERXVNQ3dHQTFVRQpBd3dsYTNWaVpYWndiaTEwY21GbVptbGpMVzFoYm1GblpYSXRZMkZBTVRjd05qazJOemN3TURBZUZ3MHlOREF5Ck1ETXhNalF4TkRCYUZ3MHlOVEF5TURJeE1qUXhOREJhTURBeExqQXNCZ05WQkFNTUpXdDFZbVYyY0c0dGRISmgKWm1acFl5MXRZVzVoWjJWeUxXTmhRREUzTURZNU5qYzNNREF3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQgpEd0F3Z2dFS0FvSUJBUURBQVpBdEZaTzJEZG9BVTUxWnRiVjI0QkVGN3RkakMzTzBPdEE2UURYTlVwNWlZZGdjCjdORVlGZE55YXltTWZMUVFGTWZqZFcxNWpDQ0N4KzFFMm1KQTVZa0ZFcXJTeDA3Z1pkKy9hcU13ZkhDT0ZTM0UKSUROdzBKYlBGVHZuSGsyZHVXby8zT1BnVmpONWw2UTBWaE10WkJEc2haVHVvSUhWaTJZcldDdnNkMU9mWFVyMwo0Y0ZJUkJ2OW5mNDIzdWthajYxdisrRDd6K3Y0bEN4R0JtUDhpYXFaNFVlckxIdWF2N1hQUnZ4QmQzNDBGY2diCm5TZVUxTXZmcTgvOUg4VTRzeWRGaUpZVUs1RFhkWU15NEw0RlMvbXZRaWR1TU5lWUw1Y2xHSXZTNGFzQjl2QlMKM0ZIY1IrQk1xVzFQWUdDc2YyL0RvdVNRVVNhcnB5VU5aczZKQWdNQkFBR2pRakJBTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJRRCsyWWFLWlA1UnU5VWdkamVnU1NiCmR1c3NaekFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBVGFNR0NLK2YxSmdKaXplVjlla3ZhckhDZHpmZzJNZkQKV2pCeFUzMXNabE1vZU9GS0hPdndjMVliTzVNTStHTGM0bGhMS2VHV1pwQmVRV0lFamo4V01wa3k2M2VtUUl5eQpOT2hjdVBUTFhCQ0JkS1lhUU1LcU9mN3c4MEw2cVRKclFER0t0a0MxVzEwbFJzbUd0TEtBbDVjU0w4VFRSZVhXCjhiNXRGOFd5Yms1Vm12VWtxdEpkSVNJTjdVOG5nV21WRUVOZFcvckNqclI5TllaSXZBZk9mS1Zrc1JuZEJaQ0kKOXdxVUI2K2JITEJBWjNpV293ZFhpRGhLMSt5Z2ZwNnpUcW9LRmxOWi8rRTNkS0tpbStyZFFGSmIvNTNvU2xaaApwMkVkT1ZNYU1mRjh1ZFhDdE44WjZnVHpPWkJxN1pmWjVpMlU1eFQ2aFNxRjFjT1ZuQS9idmc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBN0ZiSHNSSWs1dUw0bnNodzhqVVoxaVpyRzRUMVZwOWpSLyt2NlQzZ0RyMDVzZEIrCnhrYnlQN04rWVhxQTF5MnJWSDRmVmpsMXNPSTRYNWJobFFJckhaT25IM1NsMUZ0bXNzMEpXV01GU3p1MHo2b2MKbXJUN29vMEdqNzVnUGczRjlLMExIK3gwYVZJWEZOcmdOc1Y0V08vejJHazhQSFVna2k2a3ZueXQ4NGJDRGVkWgpHNjk3WjBtaWFtcTVkMmlEdXUwYUwzR0o1UXc2Rmp0YVNsVVVYVFZRdVk3cGd3VFNJRFk1MGlPdlFOYThPTFF5Ck5MKzlXRGhZWEc3dE9xbEx3akVrYkJxc254emNpRm94S0s4dkdUTlV5M3E3MmpUTDJVRWhjRU9qVVF5eG1ySHAKdEh1ZUZucDV6K1BXUnhrR2pmWG02enlRWi80endzN2VrQXRoL1FJREFRQUJBb0lCQVFEWkRaWVdsS0JaZ0Nodgp3NHlmbFk4bDgyQzVCSEpCM041VWVJbjVmejh3cWk2N2xNMXBraXpYdmlTYXArUitPczQ0S2lEamtwLzVGTHBMCmFBbkRUUnVGN1Y0MmNHNEFTdlZWenlMLytnWVpvenNhNFpPbHJnUFF0UTVLbzhCR0hXWXBvV2N2S1gxOFlNMGIKOVN5b2dORlhkUUNSUjR6dnhXNWxjdnNRaXZkRFNFTUJhbW00bFpEM0ZtUm5HVGlpaUVNSis2SFdlR1lBS1RMSgoxN0NnejZaWjg1bGtUZ0dxeEUrWkQwNDJGYWdJZlJORVI0QmZOMlp6NU5CU3RnMTJFdUpXWmRGcWpxSHlwbnNjCjNjbEd0U1Z5VStvWUFUWnV5Y2VMNVIwZUdzdTB6ZHhLT3ZzSm9yVWZ0dlMrUGovclJxWHVjOVdXSkFLU1FDVm0Ka1I1Y2M4ak5Bb0dCQU8wYkVrNTdtZWYwcXNKT0U3TFlFV1hRRFZiTmhnZ1E2eTlBZlNnVjZDMFFDdC93YkVGaQo0Rm41bTdhSHdqZUJ5OFJnMGhGbTdVNThCb3FyNnBQNFp6MEhwY1ZSTmVLeTF6R0wreFRJRXFnTXQxei9TYVE0CkIwWEZ4Ulg3d2pjeit2OC9GOVdsOElLbHhBWjhxNXd6aHNFUVVYcVIxTzF1T2FjRktSdXg3OU1UQW9HQkFQOHMKRVJBa1R3WEV3UU9ya2dQOW5tTHZLYXMwM0J6UXUrOFBtQWlsOGFmbVR5ZEFWdzJKaHBwOFlUQzl6NDM3VXU4Ngpta2lOVHpRL3MvQ1lCNEdJVVFCMEdlTDJtc2VjdWNaUHhTSW10dElSOWY4bjk2NEpuL3RtVUd4VXRFaWhWdER4ClZCdFBiWmNzc2E5VVVCRFVqRnZJSUdPTGlqSVdxbW8zM3htT0tJaXZBb0dCQU5HV2k0RWFtdnBCK1N1V3JxejUKZDYrQzBEZTVwcys4Zk5nZzdrRWYxRUw1R2xQSGh6bnBPQjN3bWFjb3JCSTZ4cTlKVW9lVmJ4RmdhcnZycVlpeApIRGtEYUpKWjdnTDlTV0YvdGlzeGkrUkdrVk5BU28xQ0JaTzBkVG13ZUlZcGlhWlUxREhENUN6b2NMVzNRRTdyCjhTTDUxTHcrNm5RU2FoM3NYdUVmVWJwSEFvR0JBTk1FNlROMUkxaDg1cldYVEJBNnk2RzdjTFVoNktsM3dRTW8KM1N6QnRyK0h5WXVIUExaNE5iVktDTUhiSm1xZkhXMnpBK1hkM2xNeUh5ZG5Ra1hQcWxUNnJuR3dTRDJ0RVVDNwp0U1hSNkR4L0YvVWpZME1zdUgyWmxnYVFZZXJ5YWE0dTlNUUZBbmNUUWZuaGVya0FYUGFGNEtzUnVYNUVtamR1Cjd2UGVTUTBIQW9HQUM0ZlJmZnFFM3RRdWxSeUJVeHhKNHlPaWJiVlpCV1hxWHRzMU0wczdsZ1YxaGVrYis1VmMKVTZ3MFh2T0pTaEZPaGF6UVdseVZUejhmSVdSa1BXa2MzSzE1dWx6cmh6NWZVa0dYOEw0OGMrTHlaSzZ1M2ZRVgpyL1pRV3JsYlZSWlhRVGhuaGhOM1Jodm96SlZZV0lpckVyMGp3VmRaQWRUYW1XZEpTQ3J4WE1NPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
# will auto generate in job
crt: ''''''
key: ''''''
serviceAccount:
# Specifies whether a service account should be created
@@ -40,20 +44,18 @@ podLabels:
podSecurityContext: { }
# fsGroup: 2000
securityContext: { }
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
runAsUser: 0
runAsGroup: 0
service:
type: ClusterIP
port8422: 8422
port9002: 9002
port10800: 10800
port10801: 10801
port80: 80
port53: 53
@@ -89,15 +91,7 @@ autoscaling:
# targetMemoryUtilizationPercentage: 80
# Additional volumes on the output Deployment definition.
volumes:
- configMap:
defaultMode: 420
items:
- key: ENVOY_CONFIG
path: envoy-config.yaml
name: kubevpn-traffic-manager
optional: false
name: envoy-config
volumes: {}
# Additional volumeMounts on the output Deployment definition.

View File

@@ -28,7 +28,6 @@ Flags:
- connect
- --kubeconfig=~/.kube/config
- --namespace=test
- --lite
---
Name: test1
@@ -62,20 +61,29 @@ func CmdAlias(f cmdutil.Factory) *cobra.Command {
If you have following config in your ~/.kubevpn/config.yaml
Name: dev
Description: This is dev k8s environment
Needs: jumper
Flags:
- connect
- --kubeconfig=~/.kube/config
- --namespace=default
- --lite
---
Name: jumper
Description: This is jumper k8s environment
Flags:
- connect
- --kubeconfig=~/.kube/jumper_config
- --namespace=test
- --extra-hosts=xxx.com
Name: all-in-one
Description: use special flags '--kubeconfig-json', no need to special kubeconfig path
Flags:
- connect
- --kubeconfig-json={"apiVersion":"v1","clusters":[{"cluster":{"certificate-authority-data":"LS0tLS1CRU..."}}]}
- --namespace=test
- --extra-hosts=xxx.com
Config file support three field: Name,Needs,Flags
@@ -84,6 +92,9 @@ func CmdAlias(f cmdutil.Factory) *cobra.Command {
# kubevpn alias jumper, just connect to cluster jumper
kubevpn alias jumper
# support special flags '--kubeconfig-json', it will save kubeconfig into ~/.kubevpn/temp/[ALIAS_NAME]
kubevpn alias all-in-one
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
if localFile != "" {
@@ -102,6 +113,10 @@ func CmdAlias(f cmdutil.Factory) *cobra.Command {
return err
}
for _, conf := range configs {
err = ParseArgs(cmd, &conf)
if err != nil {
return err
}
c := exec.Command(name, conf.Flags...)
c.Stdout = os.Stdout
c.Stdin = os.Stdin
@@ -119,7 +134,7 @@ func CmdAlias(f cmdutil.Factory) *cobra.Command {
return nil
},
}
cmd.Flags().StringVarP(&localFile, "kubevpnconfig", "f", util.If(os.Getenv("KUBEVPNCONFIG") != "", os.Getenv("KUBEVPNCONFIG"), config.GetConfigFilePath()), "Path to the kubevpnconfig file to use for CLI requests.")
cmd.Flags().StringVarP(&localFile, "kubevpnconfig", "f", util.If(os.Getenv("KUBEVPNCONFIG") != "", os.Getenv("KUBEVPNCONFIG"), config.GetConfigFile()), "Path to the kubevpnconfig file to use for CLI requests.")
cmd.Flags().StringVarP(&remoteAddr, "remote", "r", "", "Remote config file, eg: https://raw.githubusercontent.com/kubenetworks/kubevpn/master/pkg/config/config.yaml")
return cmd
}
@@ -135,7 +150,7 @@ func ParseAndGet(localFile, remoteAddr string, aliasName string) ([]Config, erro
path = remoteAddr
content, err = util.DownloadFileStream(path)
} else {
path = config.GetConfigFilePath()
path = config.GetConfigFile()
content, err = os.ReadFile(path)
}
if err != nil {

View File

@@ -1,194 +0,0 @@
package cmds
import (
"context"
"fmt"
"os"
pkgerr "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
utilcomp "k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
"github.com/wencaiwulue/kubevpn/v2/pkg/util/regctl"
)
// CmdClone multiple cluster operate, can start up one deployment to another cluster
// kubectl exec POD_NAME -c CONTAINER_NAME /sbin/killall5 or ephemeralcontainers
func CmdClone(f cmdutil.Factory) *cobra.Command {
var options = handler.CloneOptions{}
var sshConf = &pkgssh.SshConfig{}
var extraRoute = &handler.ExtraRouteInfo{}
var transferImage bool
var syncDir string
var imagePullSecretName string
cmd := &cobra.Command{
Use: "clone",
Short: i18n.T("Clone workloads to run in target-kubeconfig cluster with same volume、env、and network"),
Long: templates.LongDesc(i18n.T(`
Clone workloads to run into target-kubeconfig cluster with same volume、env、and network
In this way, you can startup another deployment in same cluster or not, but with different image version,
it also supports service mesh proxy. only traffic with special header will hit to cloned_resource.
`)),
Example: templates.Examples(i18n.T(`
# clone
- clone deployment run into current cluster and current namespace
kubevpn clone deployment/productpage
- clone deployment run into current cluster with different namespace
kubevpn clone deployment/productpage -n test
- clone deployment run into another cluster
kubevpn clone deployment/productpage --target-kubeconfig ~/.kube/other-kubeconfig
- clone multiple workloads run into current cluster and current namespace
kubevpn clone deployment/authors deployment/productpage
or
kubevpn clone deployment authors productpage
# clone with mesh, traffic with header foo=bar, will hit cloned workloads, otherwise hit origin workloads
kubevpn clone deployment/productpage --headers foo=bar
# clone workloads which api-server behind of bastion host or ssh jump host
kubevpn clone deployment/productpage --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem --headers foo=bar
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn clone service/productpage --ssh-alias <alias> --headers foo=bar
# Support ssh auth GSSAPI
kubevpn clone service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab
kubevpn clone service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache
kubevpn clone service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD>
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
plog.InitLoggerForClient()
// startup daemon process and sudo process
err = daemon.StartupDaemon(cmd.Context())
if err != nil {
return err
}
if transferImage {
err = regctl.TransferImageWithRegctl(cmd.Context(), config.OriginImage, config.Image)
}
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
_, _ = fmt.Fprintf(os.Stdout, "You must specify the type of resource to proxy. %s\n\n", cmdutil.SuggestAPIResources("kubevpn"))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
// special empty string, eg: --target-registry ""
options.IsChangeTargetRegistry = cmd.Flags().Changed("target-registry")
if syncDir != "" {
local, remote, err := util.ParseDirMapping(syncDir)
if err != nil {
return pkgerr.Wrapf(err, "options 'sync' is invalid, %s", syncDir)
}
options.LocalDir = local
options.RemoteDir = remote
} else {
options.RemoteDir = config.DefaultRemoteDir
}
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
if !sshConf.IsEmpty() {
if ip := util.GetAPIServerFromKubeConfigBytes(bytes); ip != nil {
extraRoute.ExtraCIDR = append(extraRoute.ExtraCIDR, ip.String())
}
}
req := &rpc.CloneRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: options.Headers,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
Engine: string(options.Engine),
SshJump: sshConf.ToRPC(),
TargetKubeconfig: options.TargetKubeconfig,
TargetNamespace: options.TargetNamespace,
TargetContainer: options.TargetContainer,
TargetImage: options.TargetImage,
TargetRegistry: options.TargetRegistry,
IsChangeTargetRegistry: options.IsChangeTargetRegistry,
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
LocalDir: options.LocalDir,
RemoteDir: options.RemoteDir,
}
cli := daemon.GetClient(false)
resp, err := cli.Clone(cmd.Context(), req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.CloneResponse](resp)
if err != nil {
if status.Code(err) == codes.Canceled {
err = remove(cli, args)
return err
}
return err
}
util.Print(os.Stdout, config.Slogan)
return nil
},
}
cmd.Flags().StringToStringVarP(&options.Headers, "headers", "H", map[string]string{}, "Traffic with special headers (use `and` to match all headers) with reverse it to target cluster cloned workloads, If not special, redirect all traffic to target cluster cloned workloads. eg: --headers foo=bar --headers env=dev")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &options.Engine)
cmd.Flags().StringVar(&options.TargetImage, "target-image", "", "Clone container use this image to startup container, if not special, use origin image")
cmd.Flags().StringVar(&options.TargetContainer, "target-container", "", "Clone container use special image to startup this container, if not special, use origin image")
cmd.Flags().StringVar(&options.TargetNamespace, "target-namespace", "", "Clone workloads in this namespace, if not special, use origin namespace")
cmd.Flags().StringVar(&options.TargetKubeconfig, "target-kubeconfig", "", "Clone workloads will create in this cluster, if not special, use origin cluster")
cmd.Flags().StringVar(&options.TargetRegistry, "target-registry", "", "Clone workloads will create this registry domain to replace origin registry, if not special, use origin registry")
cmd.Flags().StringVar(&syncDir, "sync", "", "Sync local dir to remote pod dir. format: LOCAL_DIR:REMOTE_DIR, eg: ~/code:/app/code")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
cmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
return cmd
}
func remove(cli rpc.DaemonClient, args []string) error {
resp, err := cli.Remove(context.Background(), &rpc.RemoveRequest{
Workloads: args,
})
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.DisconnectResponse](resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
}
return err
}
return nil
}

View File

@@ -1,100 +0,0 @@
package cmds
import (
"fmt"
"os"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdConfig(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Short: "Proxy kubeconfig which behind of ssh jump server",
}
cmd.AddCommand(cmdConfigAdd(f))
cmd.AddCommand(cmdConfigRemove(f))
return cmd
}
func cmdConfigAdd(f cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
cmd := &cobra.Command{
Use: "add",
Short: i18n.T("Proxy kubeconfig"),
Long: templates.LongDesc(i18n.T(`proxy kubeconfig which behind of ssh jump server`)),
Example: templates.Examples(i18n.T(`
# proxy api-server which api-server behind of bastion host or ssh jump host
kubevpn config add --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn config add --ssh-alias <alias>
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
// startup daemon process and sudo process
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
req := &rpc.ConfigAddRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
SshJump: sshConf.ToRPC(),
}
cli := daemon.GetClient(false)
resp, err := cli.ConfigAdd(cmd.Context(), req)
if err != nil {
return err
}
_, _ = fmt.Fprint(os.Stdout, resp.ClusterID)
return nil
},
}
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
return cmd
}
func cmdConfigRemove(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "remove",
Short: i18n.T("Remove proxy kubeconfig"),
Long: templates.LongDesc(i18n.T(`
Remove proxy kubeconfig which behind of ssh jump server
`)),
Example: templates.Examples(i18n.T(`
# remove proxy api-server which api-server behind of bastion host or ssh jump host
kubevpn config remove --kubeconfig /var/folders/30/cmv9c_5j3mq_kthx63sb1t5c0000gn/T/947048961.kubeconfig
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
// startup daemon process and sudo process
return daemon.StartupDaemon(cmd.Context())
},
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.ExactArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
req := &rpc.ConfigRemoveRequest{
ClusterID: args[0],
}
cli := daemon.GetClient(false)
_, err := cli.ConfigRemove(cmd.Context(), req)
if err != nil {
return err
}
return nil
},
}
return cmd
}

View File

@@ -26,22 +26,21 @@ import (
)
func CmdConnect(f cmdutil.Factory) *cobra.Command {
var connect = &handler.ConnectOptions{}
var extraRoute = &handler.ExtraRouteInfo{}
var sshConf = &pkgssh.SshConfig{}
var transferImage, foreground, lite bool
var transferImage, foreground bool
var imagePullSecretName string
var managerNamespace string
cmd := &cobra.Command{
Use: "connect",
Short: i18n.T("Connect to kubernetes cluster network"),
Long: templates.LongDesc(i18n.T(`
Connect to kubernetes cluster network
After connect to kubernetes cluster network, you can ping PodIP or
curl ServiceIP in local PC, it also supports k8s DNS resolve.
Like: curl authors/authors.default/authors.default.svc/authors.default.svc.cluster.local.
So you can start up your application in local PC. depends on anything in
k8s cluster is ok, connect to them just like in k8s cluster.
Upon establishing a connection to the Kubernetes cluster network, you can directly access Pod IPs and Service IPs from your local machine.
This includes capabilities such as ping Pod IPs or curl Service IPs.
Full Kubernetes DNS resolution is supported, enabling access via standard naming conventions,
This allows you to run applications locally while seamlessly connecting to all resources within the Kubernetes cluster, as if operating inside the cluster itself.
`)),
Example: templates.Examples(i18n.T(`
# Connect to k8s cluster network
@@ -90,7 +89,6 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
KubeconfigBytes: string(bytes),
Namespace: ns,
ExtraRoute: extraRoute.ToRPC(),
Engine: string(connect.Engine),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
SshJump: sshConf.ToRPC(),
@@ -98,28 +96,34 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
ManagerNamespace: managerNamespace,
}
// if is foreground, send to sudo daemon server
cli := daemon.GetClient(false)
var resp grpc.ClientStream
if lite {
resp, err = cli.ConnectFork(cmd.Context(), req)
} else {
resp, err = cli.Connect(cmd.Context(), req)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
var resp grpc.BidiStreamingClient[rpc.ConnectRequest, rpc.ConnectResponse]
resp, err = cli.Connect(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.ConnectResponse](resp)
err = util.PrintGRPCStream[rpc.ConnectResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
err = disconnect(cli, bytes, ns, sshConf)
return err
return nil
}
return err
}
if !foreground {
util.Print(os.Stdout, config.Slogan)
_, _ = fmt.Fprintln(os.Stdout, config.Slogan)
} else {
<-cmd.Context().Done()
err = disconnect(cli, bytes, ns, sshConf)
@@ -131,9 +135,9 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
return nil
},
}
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &connect.Engine)
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName)
cmd.Flags().BoolVar(&foreground, "foreground", false, "Hang up")
cmd.Flags().BoolVar(&lite, "lite", false, "connect to multiple cluster in lite mode. mode \"lite\": design for only connecting to multiple cluster network. mode \"full\": not only connect to cluster network, it also supports proxy workloads inbound traffic to local PC.")
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
@@ -141,7 +145,12 @@ func CmdConnect(f cmdutil.Factory) *cobra.Command {
}
func disconnect(cli rpc.DaemonClient, bytes []byte, ns string, sshConf *pkgssh.SshConfig) error {
resp, err := cli.Disconnect(context.Background(), &rpc.DisconnectRequest{
resp, err := cli.Disconnect(context.Background())
if err != nil {
plog.G(context.Background()).Errorf("Disconnect error: %v", err)
return err
}
err = resp.Send(&rpc.DisconnectRequest{
KubeconfigBytes: ptr.To(string(bytes)),
Namespace: ptr.To(ns),
SshJump: sshConf.ToRPC(),
@@ -150,7 +159,7 @@ func disconnect(cli rpc.DaemonClient, bytes []byte, ns string, sshConf *pkgssh.S
plog.G(context.Background()).Errorf("Disconnect error: %v", err)
return err
}
err = util.PrintGRPCStream[rpc.DisconnectResponse](resp)
err = util.PrintGRPCStream[rpc.DisconnectResponse](nil, resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -0,0 +1,98 @@
package cmds
import (
"bytes"
"fmt"
"os"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/printers"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
)
func CmdConnection(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "connection",
Short: "Connection management",
Aliases: []string{"conn"},
}
cmd.AddCommand(cmdConnectionList(f))
cmd.AddCommand(cmdConnectionUse(f))
return cmd
}
func cmdConnectionList(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: i18n.T("List all connections"),
Aliases: []string{"ls"},
Long: templates.LongDesc(i18n.T(`List all connections connected to cluster network`)),
Example: templates.Examples(i18n.T(`
# list all connections
kubevpn connection ls
# list connections by alias conn
kubevpn conn ls
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
// startup daemon process and sudo process
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
req := &rpc.ConnectionListRequest{}
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
resp, err := cli.ConnectionList(cmd.Context(), req)
if err != nil {
return err
}
var sb = new(bytes.Buffer)
w := printers.GetNewTabWriter(sb)
genConnectMsg(w, resp.CurrentConnectionID, resp.List)
_ = w.Flush()
_, _ = fmt.Fprint(os.Stdout, sb.String())
return nil
},
}
return cmd
}
func cmdConnectionUse(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "use",
Short: i18n.T("Use a specific connection"),
Long: templates.LongDesc(i18n.T(`
Use a specific connection.
`)),
Example: templates.Examples(i18n.T(`
# use a specific connection, change current connection to special id, cmd sync/unsync will use this connection
kubevpn connection use 03dc50feb8c3
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
// startup daemon process and sudo process
return daemon.StartupDaemon(cmd.Context())
},
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.ExactArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
req := &rpc.ConnectionUseRequest{
ConnectionID: args[0],
}
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
_, err = cli.ConnectionUse(cmd.Context(), req)
if err != nil {
return err
}
return nil
},
}
return cmd
}

View File

@@ -17,11 +17,8 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdControlPlane(_ cmdutil.Factory) *cobra.Command {
var (
watchDirectoryFilename string
port uint = 9002
)
func CmdControlPlane(f cmdutil.Factory) *cobra.Command {
var port uint = 9002
cmd := &cobra.Command{
Use: "control-plane",
Hidden: true,
@@ -38,11 +35,10 @@ func CmdControlPlane(_ cmdutil.Factory) *cobra.Command {
}
plog.G(context.Background()).Fatal(dns.ListenAndServe("udp", ":53", conf))
}()
err := controlplane.Main(cmd.Context(), watchDirectoryFilename, port, plog.G(context.Background()))
err := controlplane.Main(cmd.Context(), f, port, plog.G(context.Background()))
return err
},
}
cmd.Flags().StringVarP(&watchDirectoryFilename, "watchDirectoryFilename", "w", "/etc/envoy/envoy-config.yaml", "full path to directory to watch for files")
cmd.Flags().BoolVar(&config.Debug, "debug", false, "true/false")
return cmd
}

View File

@@ -1,140 +0,0 @@
package cmds
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/cp"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
)
var cpExample = templates.Examples(i18n.T(`
# !!!Important Note!!!
# Requires that the 'tar' binary is present in your container
# image. If 'tar' is not present, 'kubectl cp' will fail.
#
# For advanced use cases, such as symlinks, wildcard expansion or
# file mode preservation, consider using 'kubectl exec'.
# Copy /tmp/foo local file to /tmp/bar in a remote pod in namespace <some-namespace>
tar cf - /tmp/foo | kubectl exec -i -n <some-namespace> <some-pod> -- tar xf - -C /tmp/bar
# Copy /tmp/foo from a remote pod to /tmp/bar locally
kubectl exec -n <some-namespace> <some-pod> -- tar cf - /tmp/foo | tar xf - -C /tmp/bar
# Copy /tmp/foo_dir local directory to /tmp/bar_dir in a remote pod in the default namespace
kubectl cp /tmp/foo_dir <some-pod>:/tmp/bar_dir
# Copy /tmp/foo local file to /tmp/bar in a remote pod in a specific container
kubectl cp /tmp/foo <some-pod>:/tmp/bar -c <specific-container>
# Copy /tmp/foo local file to /tmp/bar in a remote pod in namespace <some-namespace>
kubectl cp /tmp/foo <some-namespace>/<some-pod>:/tmp/bar
# Copy /tmp/foo from a remote pod to /tmp/bar locally
kubectl cp <some-namespace>/<some-pod>:/tmp/foo /tmp/bar
# copy reverse proxy api-server behind of bastion host or ssh jump host
kubevpn cp deployment/productpage --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn cp deployment/productpage:/tmp/foo /tmp/bar --ssh-alias <alias>
# Support ssh auth GSSAPI
kubevpn cp deployment/productpage:/tmp/foo /tmp/bar --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab
kubevpn cp deployment/productpage:/tmp/foo /tmp/bar --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache
kubevpn cp deployment/productpage:/tmp/foo /tmp/bar --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD>
`,
))
func CmdCp(f cmdutil.Factory) *cobra.Command {
o := cp.NewCopyOptions(genericiooptions.IOStreams{
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
})
var sshConf = &pkgssh.SshConfig{}
cmd := &cobra.Command{
Use: "cp <file-spec-src> <file-spec-dest>",
DisableFlagsInUseLine: true,
Hidden: true,
Short: i18n.T("Copy files and directories to and from containers"),
Long: i18n.T("Copy files and directories to and from containers. Different between kubectl cp is it will de-reference symbol link."),
Example: cpExample,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
cmdutil.CheckErr(pkgssh.SshJumpAndSetEnv(cmd.Context(), sshConf, cmd.Flags(), false))
var comps []string
if len(args) == 0 {
if strings.IndexAny(toComplete, "/.~") == 0 {
// Looks like a path, do nothing
} else if strings.Contains(toComplete, ":") {
// TODO: complete remote files in the pod
} else if idx := strings.Index(toComplete, "/"); idx > 0 {
// complete <namespace>/<pod>
namespace := toComplete[:idx]
template := "{{ range .items }}{{ .metadata.namespace }}/{{ .metadata.name }}: {{ end }}"
comps = completion.CompGetFromTemplate(&template, f, namespace, []string{"pod"}, toComplete)
} else {
// Complete namespaces followed by a /
for _, ns := range completion.CompGetResource(f, "namespace", toComplete) {
comps = append(comps, fmt.Sprintf("%s/", ns))
}
// Complete pod names followed by a :
for _, pod := range completion.CompGetResource(f, "pod", toComplete) {
comps = append(comps, fmt.Sprintf("%s:", pod))
}
// Finally, provide file completion if we need to.
// We only do this if:
// 1- There are other completions found (if there are no completions,
// the shell will do file completion itself)
// 2- If there is some input from the user (or else we will end up
// listing the entire content of the current directory which could
// be too many choices for the user)
if len(comps) > 0 && len(toComplete) > 0 {
if files, err := os.ReadDir("."); err == nil {
for _, file := range files {
filename := file.Name()
if strings.HasPrefix(filename, toComplete) {
if file.IsDir() {
filename = fmt.Sprintf("%s/", filename)
}
// We are completing a file prefix
comps = append(comps, filename)
}
}
}
} else if len(toComplete) == 0 {
// If the user didn't provide any input to complete,
// we provide a hint that a path can also be used
comps = append(comps, "./", "/")
}
}
}
return comps, cobra.ShellCompDirectiveNoSpace
},
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
cmdutil.AddContainerVarFlags(cmd, &o.Container, o.Container)
cmd.Flags().BoolVarP(&o.NoPreserve, "no-preserve", "", false, "The copied file/directory's ownership and permissions will not be preserved in the container")
cmd.Flags().IntVarP(&o.MaxTries, "retries", "", 0, "Set number of retries to complete a copy operation from a container. Specify 0 to disable or any negative value for infinite retrying. The default is 0 (no retry).")
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
return cmd
}

View File

@@ -16,12 +16,11 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/action"
"github.com/wencaiwulue/kubevpn/v2/pkg/dns"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdDaemon(_ cmdutil.Factory) *cobra.Command {
func CmdDaemon(cmdutil.Factory) *cobra.Command {
var opt = &daemon.SvrOption{}
cmd := &cobra.Command{
Use: "daemon",
@@ -38,11 +37,12 @@ func CmdDaemon(_ cmdutil.Factory) *cobra.Command {
go util.StartupPProf(config.SudoPProfPort)
_ = os.RemoveAll("/etc/resolver")
_ = dns.CleanupHosts()
_ = util.CleanupTempKubeConfigFile()
// no delete temp kubeconfig because maybe recover from DB
//_ = util.CleanupTempKubeConfigFile()
} else {
go util.StartupPProf(config.PProfPort)
}
return initLogfile(action.GetDaemonLogPath())
return initLogfile(config.GetDaemonLogPath(opt.IsSudo))
},
RunE: func(cmd *cobra.Command, args []string) (err error) {
defer opt.Stop()
@@ -53,7 +53,7 @@ func CmdDaemon(_ cmdutil.Factory) *cobra.Command {
if opt.IsSudo {
for _, profile := range pprof.Profiles() {
func() {
file, e := os.Create(filepath.Join(config.PprofPath, profile.Name()))
file, e := os.Create(filepath.Join(config.GetPProfPath(), profile.Name()))
if e != nil {
return
}

View File

@@ -1,154 +0,0 @@
package cmds
import (
"context"
"fmt"
"os"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/dev"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
"github.com/wencaiwulue/kubevpn/v2/pkg/util/regctl"
)
func CmdDev(f cmdutil.Factory) *cobra.Command {
var options = &dev.Options{
NoProxy: false,
ExtraRouteInfo: handler.ExtraRouteInfo{},
}
var sshConf = &pkgssh.SshConfig{}
var transferImage bool
var imagePullSecretName string
cmd := &cobra.Command{
Use: "dev TYPE/NAME [-c CONTAINER] [flags] -- [args...]",
Short: i18n.T("Startup your kubernetes workloads in local Docker container"),
Long: templates.LongDesc(i18n.T(`
Startup your kubernetes workloads in local Docker container with same volume、env、and network
## What did it do:
- Download volume which MountPath point to, mount to docker container
- Connect to cluster network, set network to docker container
- Get all environment with command (env), set env to docker container
`)),
Example: templates.Examples(i18n.T(`
# Develop workloads
- develop deployment
kubevpn dev deployment/productpage
- develop service
kubevpn dev service/productpage
# Develop workloads with mesh, traffic with header foo=bar, will hit local PC, otherwise no effect
kubevpn dev service/productpage --headers foo=bar
# Develop workloads without proxy traffic
kubevpn dev service/productpage --no-proxy
# Develop workloads which api-server behind of bastion host or ssh jump host
kubevpn dev deployment/productpage --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn dev deployment/productpage --ssh-alias <alias>
# Switch to terminal mode; send stdin to 'bash' and sends stdout/stderror from 'bash' back to the client
kubevpn dev deployment/authors -n default --kubeconfig ~/.kube/config --ssh-alias dev --entrypoint /bin/bash
or
kubevpn dev deployment/authors -n default --kubeconfig ~/.kube/config --ssh-alias dev --entrypoint /bin/bash
# Support ssh auth GSSAPI
kubevpn dev deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab --entrypoint /bin/bash
kubevpn dev deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache --entrypoint /bin/bash
kubevpn dev deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD> --entrypoint /bin/bash
`)),
ValidArgsFunction: completion.ResourceTypeAndNameCompletionFunc(f),
Args: cobra.MatchAll(cobra.OnlyValidArgs),
DisableFlagsInUseLine: true,
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
_, _ = fmt.Fprintf(os.Stdout, "You must specify the type of resource to proxy. %s\n\n", cmdutil.SuggestAPIResources("kubevpn"))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
err := cmd.Flags().Parse(args[1:])
if err != nil {
return err
}
plog.InitLoggerForClient()
err = daemon.StartupDaemon(cmd.Context())
if err != nil {
return err
}
if transferImage {
err = regctl.TransferImageWithRegctl(cmd.Context(), config.OriginImage, config.Image)
}
if err != nil {
return err
}
return pkgssh.SshJumpAndSetEnv(cmd.Context(), sshConf, cmd.Flags(), false)
},
RunE: func(cmd *cobra.Command, args []string) error {
options.Workload = args[0]
for i, arg := range args {
if arg == "--" && i != len(args)-1 {
options.ContainerOptions.Args = args[i+1:]
break
}
}
defer func() {
for _, function := range options.GetRollbackFuncList() {
if function != nil {
if err := function(); err != nil {
plog.G(context.Background()).Errorf("Rollback failed, error: %s", err.Error())
}
}
}
}()
if err := options.InitClient(f); err != nil {
return err
}
conf, hostConfig, err := dev.Parse(cmd.Flags(), options.ContainerOptions)
if err != nil {
return err
}
return options.Main(cmd.Context(), sshConf, conf, hostConfig, imagePullSecretName)
},
}
cmd.Flags().SortFlags = false
cmd.Flags().StringToStringVarP(&options.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().BoolVar(&options.NoProxy, "no-proxy", false, "Whether proxy remote workloads traffic into local or not, true: just startup container on local without inject containers to intercept traffic, false: intercept traffic and forward to local")
cmdutil.AddContainerVarFlags(cmd, &options.ContainerName, options.ContainerName)
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", completion.ContainerCompletionFunc(f)))
cmd.Flags().StringVar((*string)(&options.ConnectMode), "connect-mode", string(dev.ConnectModeHost), "Connect to kubernetes network in container or in host, eg: ["+string(dev.ConnectModeContainer)+"|"+string(dev.ConnectModeHost)+"]")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &options.Engine)
// diy docker options
cmd.Flags().StringVar(&options.DevImage, "dev-image", "", "Use to startup docker container, Default is pod image")
// -- origin docker options -- start
options.ContainerOptions = dev.AddFlags(cmd.Flags())
cmd.Flags().StringVar(&options.RunOptions.Pull, "pull", dev.PullImageMissing, `Pull image before running ("`+dev.PullImageAlways+`"|"`+dev.PullImageMissing+`"|"`+dev.PullImageNever+`")`)
command.AddPlatformFlag(cmd.Flags(), &options.RunOptions.Platform)
// -- origin docker options -- end
handler.AddExtraRoute(cmd.Flags(), &options.ExtraRouteInfo)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
return cmd
}

View File

@@ -1,9 +1,9 @@
package cmds
import (
"context"
"fmt"
"os"
"strconv"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
@@ -21,7 +21,6 @@ import (
func CmdDisconnect(f cmdutil.Factory) *cobra.Command {
var all = false
var clusterIDs []string
cmd := &cobra.Command{
Use: "disconnect",
Short: i18n.T("Disconnect from kubernetes cluster network"),
@@ -30,12 +29,15 @@ func CmdDisconnect(f cmdutil.Factory) *cobra.Command {
This command is to disconnect from cluster. after use command 'kubevpn connect',
you can use this command to disconnect from a specific cluster.
before disconnect, it will leave proxy resource and clone resource if resource depends on this cluster
after disconnect it will also cleanup DNS and host
- Before disconnect, it will leave proxy resource and sync resource if resource depends on this cluster.
- After disconnect, it will also cleanup DNS and host.
`)),
Example: templates.Examples(i18n.T(`
# disconnect from cluster network and restore proxy resource
kubevpn disconnect
# disconnect from special connection id
kubevpn disconnect 03dc50feb8c3
# disconnect from all cluster
kubevpn disconnect --all
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
plog.InitLoggerForClient()
@@ -44,35 +46,30 @@ func CmdDisconnect(f cmdutil.Factory) *cobra.Command {
},
Args: cobra.MatchAll(cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 && all {
return fmt.Errorf("either specify --all or ID, not both")
if len(args) == 0 && !all {
return fmt.Errorf("either specify --all or connecetion ID")
}
if len(clusterIDs) > 0 && all {
return fmt.Errorf("either specify --all or cluster-id, not both")
}
if len(args) == 0 && !all && len(clusterIDs) == 0 {
return fmt.Errorf("either specify --all or ID or cluster-id")
}
var ids *int32
var id string
if len(args) > 0 {
integer, err := strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid ID: %s: %v", args[0], err)
}
ids = pointer.Int32(int32(integer))
id = args[0]
}
client, err := daemon.GetClient(false).Disconnect(
cmd.Context(),
&rpc.DisconnectRequest{
ID: ids,
ClusterIDs: clusterIDs,
All: pointer.Bool(all),
},
)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.DisconnectResponse](client)
req := &rpc.DisconnectRequest{
ConnectionID: &id,
All: pointer.Bool(all),
}
resp, err := cli.Disconnect(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.DisconnectResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
@@ -84,6 +81,5 @@ func CmdDisconnect(f cmdutil.Factory) *cobra.Command {
},
}
cmd.Flags().BoolVar(&all, "all", all, "Disconnect all cluster, disconnect from all cluster network")
cmd.Flags().StringArrayVar(&clusterIDs, "cluster-id", []string{}, "Cluster id, command status -o yaml/json will show cluster-id")
return cmd
}

View File

@@ -1,109 +0,0 @@
package cmds
import (
"cmp"
"encoding/json"
"os"
"slices"
"strings"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/printers"
cmdget "k8s.io/kubectl/pkg/cmd/get"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
)
func CmdGet(f cmdutil.Factory) *cobra.Command {
var printFlags = cmdget.NewGetPrintFlags()
cmd := &cobra.Command{
Use: "get",
Hidden: true,
Short: i18n.T("Get cluster resources which connected"),
Long: templates.LongDesc(i18n.T(`Get cluster resources which connected`)),
Example: templates.Examples(i18n.T(`
# Get resource to k8s cluster network
kubevpn get pods
# Get api-server behind of bastion host or ssh jump host
kubevpn get deployment --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn get service --ssh-alias <alias>
`)),
PreRunE: func(cmd *cobra.Command, args []string) error {
// startup daemon process and sudo process
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
ns, _, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
client, err := daemon.GetClient(true).Get(
cmd.Context(),
&rpc.GetRequest{
Namespace: ns,
Resource: args[0],
},
)
if err != nil {
return err
}
w := printers.GetNewTabWriter(os.Stdout)
var toPrinter = func() (printers.ResourcePrinterFunc, error) {
var flags = printFlags.Copy()
_ = flags.EnsureWithNamespace()
printer, err := flags.ToPrinter()
if err != nil {
return nil, err
}
printer, err = printers.NewTypeSetter(scheme.Scheme).WrapToPrinter(printer, nil)
if err != nil {
return nil, err
}
outputOption := cmd.Flags().Lookup("output").Value.String()
if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
} else {
printer = &cmdget.TablePrinter{Delegate: printer}
}
return printer.PrintObj, nil
}
var list []*v1.PartialObjectMetadata
for _, m := range client.Metadata {
var data v1.PartialObjectMetadata
err = json.Unmarshal([]byte(m), &data)
if err != nil {
continue
}
list = append(list, &data)
}
slices.SortStableFunc(list, func(a, b *v1.PartialObjectMetadata) int {
compare := cmp.Compare(a.GetNamespace(), b.GetNamespace())
if compare == 0 {
return cmp.Compare(a.GetName(), b.GetName())
}
return compare
})
for _, m := range list {
printer, err := toPrinter()
if err != nil {
return err
}
_ = printer.PrintObj(m, w)
}
return w.Flush()
},
}
printFlags.AddFlags(cmd)
return cmd
}

View File

@@ -8,10 +8,10 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/util/regctl"
)
func CmdImageCopy(_ cmdutil.Factory) *cobra.Command {
func CmdImageCopy(cmdutil.Factory) *cobra.Command {
var imageCmd = &cobra.Command{
Use: "image <cmd>",
Short: "copy images",
Short: "Copy images",
}
copyCmd := &cobra.Command{

View File

@@ -1,6 +1,8 @@
package cmds
import (
"context"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -30,17 +32,32 @@ func CmdLeave(f cmdutil.Factory) *cobra.Command {
# leave proxy resource and restore it to origin
kubevpn leave deployment/authors
`)),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
leave, err := daemon.GetClient(false).Leave(cmd.Context(), &rpc.LeaveRequest{
Workloads: args,
})
_, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LeaveResponse](leave)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
req := &rpc.LeaveRequest{
Namespace: ns,
Workloads: args,
}
resp, err := cli.Leave(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LeaveResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -1,41 +0,0 @@
package cmds
import (
"fmt"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
)
func CmdList(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: i18n.T("List proxy resources"),
Long: templates.LongDesc(i18n.T(`List proxy resources`)),
Example: templates.Examples(i18n.T(`
# list proxy resources
kubevpn list
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := daemon.GetClient(true).List(
cmd.Context(),
&rpc.ListRequest{},
)
if err != nil {
return err
}
fmt.Println(client.GetMessage())
return nil
},
Hidden: true,
}
return cmd
}

View File

@@ -34,11 +34,19 @@ func CmdLogs(f cmdutil.Factory) *cobra.Command {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := daemon.GetClient(true).Logs(cmd.Context(), req)
cli, err := daemon.GetClient(true)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LogResponse](client)
resp, err := cli.Logs(cmd.Context())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LogResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
@@ -49,6 +57,6 @@ func CmdLogs(f cmdutil.Factory) *cobra.Command {
},
}
cmd.Flags().BoolVarP(&req.Follow, "follow", "f", false, "Specify if the logs should be streamed.")
cmd.Flags().Int32VarP(&req.Lines, "number", "N", 10, "Lines of recent log file to display.")
cmd.Flags().Int32VarP(&req.Lines, "lines", "l", 10, "Lines of recent log file to display.")
return cmd
}

26
cmd/kubevpn/cmds/once.go Normal file
View File

@@ -0,0 +1,26 @@
package cmds
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
)
func CmdOnce(factory cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "once",
Short: i18n.T("Once generate TLS and restart deployment"),
Long: templates.LongDesc(i18n.T(`Once generate TLS and restart deployment for helm installation.`)),
RunE: func(cmd *cobra.Command, args []string) (err error) {
return handler.Once(cmd.Context(), factory)
},
Hidden: true,
DisableFlagsInUseLine: true,
}
cmd.Flags().StringVar(&config.Image, "image", config.Image, "use this image to startup container")
return cmd
}

View File

@@ -25,11 +25,13 @@ import (
)
func CmdProxy(f cmdutil.Factory) *cobra.Command {
var connect = handler.ConnectOptions{}
var headers = make(map[string]string)
var portmap []string
var extraRoute = &handler.ExtraRouteInfo{}
var sshConf = &pkgssh.SshConfig{}
var transferImage, foreground bool
var imagePullSecretName string
var managerNamespace string
cmd := &cobra.Command{
Use: "proxy",
Short: i18n.T("Proxy kubernetes workloads inbound traffic into local PC"),
@@ -57,10 +59,10 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
kubevpn proxy deployment authors productpage
# Reverse proxy with mesh, traffic with header foo=bar, will hit local PC, otherwise no effect
kubevpn proxy service/productpage --headers foo=bar
kubevpn proxy deployment/productpage --headers foo=bar
# Reverse proxy with mesh, traffic with header foo=bar and env=dev, will hit local PC, otherwise no effect
kubevpn proxy service/productpage --headers foo=bar --headers env=dev
kubevpn proxy deployment/productpage --headers foo=bar --headers env=dev
# 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 foo=bar
@@ -69,12 +71,12 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn proxy service/productpage --ssh-alias <alias> --headers foo=bar
kubevpn proxy deployment/productpage --ssh-alias <alias> --headers foo=bar
# Support ssh auth GSSAPI
kubevpn proxy service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab
kubevpn proxy service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache
kubevpn proxy service/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD>
kubevpn proxy deployment/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab
kubevpn proxy deployment/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache
kubevpn proxy deployment/productpage --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD>
# Support port map, you can proxy container port to local port by command:
kubevpn proxy deployment/productpage --portmap 80:8080
@@ -88,6 +90,7 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
# Auto proxy container port to same local port, and auto detect protocol
kubevpn proxy deployment/productpage
`)),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
plog.InitLoggerForClient()
if err = daemon.StartupDaemon(cmd.Context()); err != nil {
@@ -99,16 +102,6 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
fmt.Fprintf(os.Stdout, "You must specify the type of resource to proxy. %s\n\n", cmdutil.SuggestAPIResources("kubevpn"))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
usageString = fmt.Sprintf("%s\nUse \"%s explain <resource>\" for a detailed description of that resource (e.g. %[2]s explain pods).", usageString, fullCmdName)
}
return cmdutil.UsageErrorf(cmd, usageString)
}
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
@@ -119,54 +112,64 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
}
}
// todo 将 doConnect 方法封装?内部使用 client 发送到daemon
cli := daemon.GetClient(false)
client, err := cli.Proxy(
cmd.Context(),
&rpc.ConnectRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: connect.Headers,
PortMap: connect.PortMap,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
Engine: string(connect.Engine),
SshJump: sshConf.ToRPC(),
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
},
)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.ConnectResponse](client)
req := &rpc.ProxyRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: headers,
PortMap: portmap,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
SshJump: sshConf.ToRPC(),
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
ManagerNamespace: managerNamespace,
}
resp, err := cli.Proxy(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.ConnectResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
err = leave(cli, args)
return err
return nil
}
return err
}
util.Print(os.Stdout, config.Slogan)
_, _ = fmt.Fprintln(os.Stdout, config.Slogan)
// hangup
if foreground {
// leave from cluster resources
<-cmd.Context().Done()
err = leave(cli, args)
err = leave(cli, ns, args)
if err != nil {
return err
}
err = disconnect(cli, bytes, ns, sshConf)
if err != nil {
return err
}
_, _ = fmt.Fprint(os.Stdout, "Disconnect completed")
}
return nil
},
}
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. format: <KEY>=<VALUE> eg: --headers foo=bar --headers env=dev")
cmd.Flags().StringArrayVar(&connect.PortMap, "portmap", []string{}, "Port map, map container port to local port, format: [tcp/udp]/containerPort:localPort, If not special, localPort will use containerPort. eg: tcp/80:8080 or udp/5000:5001 or 80 or 80:8080")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName, &connect.Engine)
cmd.Flags().StringToStringVarP(&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. format: <KEY>=<VALUE> eg: --headers foo=bar --headers env=dev")
cmd.Flags().StringArrayVar(&portmap, "portmap", []string{}, "Port map, map container port to local port, format: [tcp/udp]/containerPort:localPort, If not special, localPort will use containerPort. eg: tcp/80:8080 or udp/5000:5001 or 80 or 80:8080")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName)
cmd.Flags().BoolVar(&foreground, "foreground", false, "foreground hang up")
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
@@ -174,14 +177,20 @@ func CmdProxy(f cmdutil.Factory) *cobra.Command {
return cmd
}
func leave(cli rpc.DaemonClient, args []string) error {
stream, err := cli.Leave(context.Background(), &rpc.LeaveRequest{
func leave(cli rpc.DaemonClient, ns string, args []string) error {
req := &rpc.LeaveRequest{
Namespace: ns,
Workloads: args,
})
}
resp, err := cli.Leave(context.Background())
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LeaveResponse](stream)
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.LeaveResponse](nil, resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -29,8 +29,8 @@ func CmdQuit(f cmdutil.Factory) *cobra.Command {
kubevpn quit
`)),
RunE: func(cmd *cobra.Command, args []string) error {
_ = quit(cmd.Context(), false)
_ = quit(cmd.Context(), true)
_ = quit(cmd.Context(), false)
util.CleanExtensionLib()
_, _ = fmt.Fprint(os.Stdout, "Exited")
return nil
@@ -40,15 +40,19 @@ func CmdQuit(f cmdutil.Factory) *cobra.Command {
}
func quit(ctx context.Context, isSudo bool) error {
cli := daemon.GetClient(isSudo)
if cli == nil {
return nil
}
client, err := cli.Quit(ctx, &rpc.QuitRequest{})
cli, err := daemon.GetClient(isSudo)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.QuitResponse](client)
resp, err := cli.Quit(context.Background())
if err != nil {
return err
}
err = resp.Send(&rpc.QuitRequest{})
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.QuitResponse](ctx, resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -1,52 +0,0 @@
package cmds
import (
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdRemove(f cmdutil.Factory) *cobra.Command {
var cmd = &cobra.Command{
Use: "remove",
Short: "Remove clone resource",
Long: templates.LongDesc(i18n.T(`
Remove clone resource
This command is design to remove clone resources, after use command 'kubevpn clone xxx',
it will generate and create a new resource in target k8s cluster with format [resource_name]_clone_xxxxx,
use this command to remove this created resources.
`)),
Example: templates.Examples(i18n.T(`
# leave proxy resources to origin
kubevpn remove deployment/authors
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
leave, err := daemon.GetClient(false).Remove(cmd.Context(), &rpc.RemoveRequest{
Workloads: args,
})
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.RemoveResponse](leave)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
}
return err
}
return nil
},
}
return cmd
}

View File

@@ -1,6 +1,8 @@
package cmds
import (
"context"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -19,9 +21,9 @@ func CmdReset(f cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
cmd := &cobra.Command{
Use: "reset",
Short: "Reset workloads to origin status",
Short: "Reset workloads to origin spec",
Long: templates.LongDesc(i18n.T(`
Reset workloads to origin status
Reset workloads to origin spec
Reset will remove injected container envoy-proxy and vpn, and restore service mesh rules.
`)),
@@ -56,18 +58,25 @@ func CmdReset(f cmdutil.Factory) *cobra.Command {
if err != nil {
return err
}
cli := daemon.GetClient(false)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
req := &rpc.ResetRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Workloads: args,
SshJump: sshConf.ToRPC(),
}
resp, err := cli.Reset(cmd.Context(), req)
resp, err := cli.Reset(context.Background())
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.ResetResponse](resp)
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.ResetResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -5,6 +5,7 @@ import (
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
restclient "k8s.io/client-go/rest"
@@ -16,6 +17,7 @@ import (
"k8s.io/utils/ptr"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func NewKubeVPNCommand() *cobra.Command {
@@ -31,7 +33,7 @@ func NewKubeVPNCommand() *cobra.Command {
}
flags := cmd.PersistentFlags()
configFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
configFlags := genericclioptions.NewConfigFlags(true)
configFlags.WrapConfigFn = func(c *rest.Config) *rest.Config {
if path, ok := os.LookupEnv(config.EnvSSHJump); ok {
kubeconfigBytes, err := os.ReadFile(path)
@@ -56,30 +58,29 @@ func NewKubeVPNCommand() *cobra.Command {
CmdDisconnect(factory),
CmdProxy(factory),
CmdLeave(factory),
CmdClone(factory),
CmdRemove(factory),
CmdDev(factory),
CmdSync(factory),
CmdUnsync(factory),
CmdRun(factory),
// Hidden, Server Commands (DO NOT USE IT !!!)
CmdControlPlane(factory),
CmdServe(factory),
CmdServer(factory),
CmdDaemon(factory),
CmdWebhook(factory),
CmdSyncthing(factory),
CmdOnce(factory),
},
},
{
Message: "Management commands:",
Commands: []*cobra.Command{
CmdStatus(factory),
CmdList(factory),
CmdAlias(factory),
CmdGet(factory),
CmdConfig(factory),
CmdConnection(factory),
CmdRoute(factory),
CmdSSH(factory),
CmdSSHDaemon(factory),
CmdImageCopy(factory),
CmdLogs(factory),
CmdCp(factory),
CmdReset(factory),
CmdUninstall(factory),
CmdQuit(factory),
@@ -99,8 +100,13 @@ func NewKubeVPNCommand() *cobra.Command {
return cmd
}
func AddKubeconfigJsonFlag(flags *pflag.FlagSet, kubeconfigJson *string) {
flags.StringVar(kubeconfigJson, "kubeconfig-json", ptr.Deref[string](kubeconfigJson, ""), "Json format of kubeconfig to use for CLI requests.")
}
type warp struct {
*genericclioptions.ConfigFlags
KubeconfigJson string
}
func (f *warp) ToRawKubeConfigLoader() clientcmd.ClientConfig {
@@ -108,5 +114,11 @@ func (f *warp) ToRawKubeConfigLoader() clientcmd.ClientConfig {
home := homedir.HomeDir()
f.KubeConfig = ptr.To(strings.Replace(*f.KubeConfig, "~", home, 1))
}
if strings.TrimSpace(f.KubeconfigJson) != "" {
path, err := util.ConvertToTempKubeconfigFile([]byte(f.KubeconfigJson), "")
if err == nil {
f.KubeConfig = ptr.To(path)
}
}
return f.ConfigFlags.ToRawKubeConfigLoader()
}

146
cmd/kubevpn/cmds/route.go Normal file
View File

@@ -0,0 +1,146 @@
package cmds
import (
"bytes"
"fmt"
"net"
"os"
"github.com/libp2p/go-netroute"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/printers"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
// CmdRoute
func CmdRoute(f cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "route",
Short: "Route table management",
}
cmd.AddCommand(CmdRouteAdd(f))
cmd.AddCommand(CmdRouteDelete(f))
cmd.AddCommand(CmdRouteSearch(f))
return cmd
}
func CmdRouteAdd(cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "add",
Short: "Add a route",
Long: templates.LongDesc(i18n.T(`
Add a route
`)),
Example: templates.Examples(i18n.T(`
# Add a route to current connection tun device
kubevpn route add 198.19.0.1/32
# Query current connection tun device
kubevpn status
`)),
PreRunE: func(cmd *cobra.Command, args []string) error {
plog.InitLoggerForClient()
return daemon.StartupDaemon(cmd.Context())
},
Args: cobra.MatchAll(cobra.ExactArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
_, cidr, err := net.ParseCIDR(args[0])
if err != nil {
return err
}
_, err = cli.Route(cmd.Context(), &rpc.RouteRequest{Cidr: cidr.String(), Type: rpc.RouteType_ROUTE_ADD})
if err != nil {
return err
}
return err
},
}
return cmd
}
func CmdRouteDelete(cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete a specific route",
Long: templates.LongDesc(i18n.T(`
Delete a specific route
`)),
Example: templates.Examples(i18n.T(`
# Delete a specific route
kubevpn route delete 198.19.0.1/32
# Query current connection tun device
kubevpn status
`)),
PreRunE: func(cmd *cobra.Command, args []string) error {
plog.InitLoggerForClient()
return daemon.StartupDaemon(cmd.Context())
},
Args: cobra.MatchAll(cobra.ExactArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
_, cidr, err := net.ParseCIDR(args[0])
if err != nil {
return err
}
_, err = cli.Route(cmd.Context(), &rpc.RouteRequest{Cidr: cidr.String(), Type: rpc.RouteType_ROUTE_DELETE})
if err != nil {
return err
}
return err
},
}
return cmd
}
func CmdRouteSearch(cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "search",
Short: "Search a specific route",
Long: templates.LongDesc(i18n.T(`
Search a specific route
`)),
Example: templates.Examples(i18n.T(`
# Search a specific route
kubevpn route search 198.19.0.1
`)),
PreRunE: func(cmd *cobra.Command, args []string) error {
plog.InitLoggerForClient()
return daemon.StartupDaemon(cmd.Context())
},
Args: cobra.MatchAll(cobra.ExactArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
dst := net.ParseIP(args[0])
if dst == nil {
return fmt.Errorf("invalid ip: %s", args[0])
}
r, err := netroute.New()
if err != nil {
return err
}
iface, gateway, src, err := r.Route(dst)
if err != nil {
return err
}
var sb = new(bytes.Buffer)
w := printers.GetNewTabWriter(sb)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", "DESTINATION", "GATEWAY", "RT_IFA", "MTU", "NETIF")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\n", dst, gateway.String(), src.String(), iface.MTU, iface.Name)
_ = w.Flush()
_, _ = fmt.Fprint(os.Stdout, sb.String())
return nil
},
}
return cmd
}

148
cmd/kubevpn/cmds/run.go Normal file
View File

@@ -0,0 +1,148 @@
package cmds
import (
"context"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/run"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
"github.com/wencaiwulue/kubevpn/v2/pkg/util/regctl"
)
func CmdRun(f cmdutil.Factory) *cobra.Command {
var options = &run.Options{
NoProxy: false,
ExtraRouteInfo: handler.ExtraRouteInfo{},
}
var sshConf = &pkgssh.SshConfig{}
var transferImage bool
var imagePullSecretName string
var managerNamespace string
cmd := &cobra.Command{
Use: "run TYPE/NAME [-c CONTAINER] [flags] -- [args...]",
Short: i18n.T("Run kubernetes workloads in local Docker container"),
Long: templates.LongDesc(i18n.T(`
Run kubernetes workloads in local Docker container with same volume、env、and network
## What did it do:
- Download volume which MountPath point to, mount to docker container
- Connect to cluster network, set network to docker container
- Get all environment with command (env), set env to docker container
`)),
Example: templates.Examples(i18n.T(`
# Run workloads
- run deployment
kubevpn run deployment/productpage
# Run workloads with mesh, traffic with header foo=bar, will hit local PC, otherwise no effect
kubevpn run deployment/productpage --headers foo=bar
# Run workloads without proxy traffic
kubevpn run deployment/productpage --no-proxy
# Run workloads which api-server behind of bastion host or ssh jump host
kubevpn run deployment/productpage --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn run deployment/productpage --ssh-alias <alias>
# Switch to terminal mode; send stdin to 'bash' and sends stdout/stderror from 'bash' back to the client
kubevpn run deployment/authors -n default --kubeconfig ~/.kube/config --ssh-alias dev --entrypoint /bin/bash
or
kubevpn run deployment/authors -n default --kubeconfig ~/.kube/config --ssh-alias dev --entrypoint /bin/bash
# Support ssh auth GSSAPI
kubevpn run deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab --entrypoint /bin/bash
kubevpn run deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache --entrypoint /bin/bash
kubevpn run deployment/authors -n default --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD> --entrypoint /bin/bash
`)),
ValidArgsFunction: completion.ResourceTypeAndNameCompletionFunc(f),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
DisableFlagsInUseLine: true,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := cmd.Flags().Parse(args[1:])
if err != nil {
return err
}
plog.InitLoggerForClient()
err = daemon.StartupDaemon(cmd.Context())
if err != nil {
return err
}
if transferImage {
err = regctl.TransferImageWithRegctl(cmd.Context(), config.OriginImage, config.Image)
}
if err != nil {
return err
}
if sshConf.IsEmpty() {
return nil
}
bytes, _, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
return pkgssh.SshJumpAndSetEnv(cmd.Context(), sshConf, bytes, false)
},
RunE: func(cmd *cobra.Command, args []string) error {
options.Workload = args[0]
if len(args) > 1 {
options.ContainerOptions.Args = args[1:]
}
if err := options.InitClient(f); err != nil {
return err
}
conf, hostConfig, err := run.Parse(cmd.Flags(), options.ContainerOptions)
if err != nil {
return err
}
defer func() {
for _, function := range options.GetRollbackFuncList() {
if function != nil {
if err := function(); err != nil {
plog.G(context.Background()).Errorf("Rollback failed, error: %s", err.Error())
}
}
}
}()
return options.Main(cmd.Context(), sshConf, conf, hostConfig, imagePullSecretName, managerNamespace)
},
}
cmd.Flags().SortFlags = false
cmd.Flags().StringToStringVarP(&options.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().BoolVar(&options.NoProxy, "no-proxy", false, "Whether proxy remote workloads traffic into local or not, true: just startup container on local without inject containers to intercept traffic, false: intercept traffic and forward to local")
cmdutil.AddContainerVarFlags(cmd, &options.ContainerName, options.ContainerName)
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", completion.ContainerCompletionFunc(f)))
cmd.Flags().StringVar((*string)(&options.ConnectMode), "connect-mode", string(run.ConnectModeHost), "Connect to kubernetes network in container or in host, eg: ["+string(run.ConnectModeContainer)+"|"+string(run.ConnectModeHost)+"]")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName)
cmd.Flags().StringVar(&managerNamespace, "manager-namespace", "", "The namespace where the traffic manager is to be found. Only works in cluster mode (install kubevpn server by helm)")
// diy docker options
cmd.Flags().StringVar(&options.DevImage, "dev-image", "", "Use to startup docker container, Default is pod image")
// -- origin docker options -- start
options.ContainerOptions = run.AddFlags(cmd.Flags())
cmd.Flags().StringVar(&options.RunOptions.Pull, "pull", run.PullImageMissing, `Pull image before running ("`+run.PullImageAlways+`"|"`+run.PullImageMissing+`"|"`+run.PullImageNever+`")`)
command.AddPlatformFlag(cmd.Flags(), &options.RunOptions.Platform)
// -- origin docker options -- end
handler.AddExtraRoute(cmd.Flags(), &options.ExtraRouteInfo)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
return cmd
}

View File

@@ -1,61 +0,0 @@
package cmds
import (
"math/rand"
"os"
"runtime"
"time"
"github.com/spf13/cobra"
"go.uber.org/automaxprocs/maxprocs"
glog "gvisor.dev/gvisor/pkg/log"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/core"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdServe(_ cmdutil.Factory) *cobra.Command {
var route = &core.Route{}
cmd := &cobra.Command{
Use: "serve",
Hidden: true,
Short: "Server side, startup traffic manager, forward inbound and outbound traffic",
Long: templates.LongDesc(i18n.T(`
Server side, startup traffic manager, forward inbound and outbound traffic.
`)),
Example: templates.Examples(i18n.T(`
# serve node
kubevpn serve -L "tcp://:10800" -L "tun://127.0.0.1:8422?net=198.19.0.123/32"
`)),
PreRun: func(*cobra.Command, []string) {
runtime.GOMAXPROCS(0)
go util.StartupPProfForServer(config.PProfPort)
glog.SetTarget(plog.ServerEmitter{Writer: &glog.Writer{Next: os.Stderr}})
},
RunE: func(cmd *cobra.Command, args []string) error {
rand.Seed(time.Now().UnixNano())
_, _ = maxprocs.Set(maxprocs.Logger(nil))
ctx := cmd.Context()
err := handler.Complete(ctx, route)
if err != nil {
return err
}
servers, err := handler.Parse(*route)
if err != nil {
plog.G(ctx).Errorf("Parse server failed: %v", err)
return err
}
return handler.Run(ctx, servers)
},
}
cmd.Flags().StringArrayVarP(&route.ServeNodes, "node", "L", []string{}, "Startup node server. eg: tcp://localhost:1080")
cmd.Flags().StringVarP(&route.ChainNode, "chain", "F", "", "Forward chain. eg: tcp://192.168.1.100:2345")
cmd.Flags().BoolVar(&config.Debug, "debug", false, "Enable debug log or not")
return cmd
}

View File

@@ -0,0 +1,59 @@
package cmds
import (
"math/rand"
"os"
"runtime"
"time"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.uber.org/automaxprocs/maxprocs"
glog "gvisor.dev/gvisor/pkg/log"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/core"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdServer(cmdutil.Factory) *cobra.Command {
var route = &core.Route{}
cmd := &cobra.Command{
Use: "server",
Hidden: true,
Short: "Server side, startup traffic manager, forward inbound and outbound traffic",
Long: templates.LongDesc(i18n.T(`
Server side, startup traffic manager, forward inbound and outbound traffic.
`)),
Example: templates.Examples(i18n.T(`
# server listener
kubevpn server -l "gtcp://:10801" -l "tun://?net=198.19.0.123/32"
`)),
PreRun: func(*cobra.Command, []string) {
runtime.GOMAXPROCS(0)
go util.StartupPProfForServer(config.PProfPort)
glog.SetTarget(plog.ServerEmitter{Writer: &glog.Writer{Next: os.Stderr}})
},
RunE: func(cmd *cobra.Command, args []string) error {
rand.Seed(time.Now().UnixNano())
_, _ = maxprocs.Set(maxprocs.Logger(nil))
ctx := cmd.Context()
logger := plog.InitLoggerForServer()
logger.SetLevel(util.If(config.Debug, log.DebugLevel, log.InfoLevel))
servers, err := handler.Parse(*route)
if err != nil {
plog.G(ctx).Errorf("Parse server failed: %v", err)
return err
}
return handler.Run(plog.WithLogger(ctx, logger), servers)
},
}
cmd.Flags().StringArrayVarP(&route.Listeners, "listener", "l", []string{}, "Startup listener server. eg: tcp://localhost:1080")
cmd.Flags().BoolVar(&config.Debug, "debug", false, "Enable debug log or not")
return cmd
}

View File

@@ -27,7 +27,7 @@ import (
// CmdSSH
// Remember to use network mask 32, because ssh using unique network CIDR 198.18.0.0/16
func CmdSSH(_ cmdutil.Factory) *cobra.Command {
func CmdSSH(cmdutil.Factory) *cobra.Command {
var sshConf = &pkgssh.SshConfig{}
var extraCIDR []string
var platform string

View File

@@ -15,7 +15,7 @@ import (
// CmdSSHDaemon
// set local tun ip 198.19.0.1/32, remember to use mask 32
func CmdSSHDaemon(_ cmdutil.Factory) *cobra.Command {
func CmdSSHDaemon(cmdutil.Factory) *cobra.Command {
var clientIP string
cmd := &cobra.Command{
Use: "ssh-daemon",
@@ -31,16 +31,15 @@ func CmdSSHDaemon(_ cmdutil.Factory) *cobra.Command {
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
client, err := daemon.GetClient(true).SshStart(
cmd.Context(),
&rpc.SshStartRequest{
ClientIP: clientIP,
},
)
cli, err := daemon.GetClient(true)
if err != nil {
return err
}
_, err = fmt.Fprint(os.Stdout, client.ServerIP)
resp, err := cli.SshStart(cmd.Context(), &rpc.SshStartRequest{ClientIP: clientIP})
if err != nil {
return err
}
_, err = fmt.Fprint(os.Stdout, resp.ServerIP)
return err
},
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/liggitt/tabwriter"
@@ -27,9 +28,10 @@ import (
)
const (
FormatJson = "json"
FormatYaml = "yaml"
FormatTable = "table"
FormatJson = "json"
FormatYaml = "yaml"
FormatTable = "table"
FormatTableWide = "wide"
)
func CmdStatus(f cmdutil.Factory) *cobra.Command {
@@ -39,16 +41,16 @@ func CmdStatus(f cmdutil.Factory) *cobra.Command {
var format string
cmd := &cobra.Command{
Use: "status",
Short: i18n.T("Show connect status and list proxy/clone resource"),
Short: i18n.T("Show connect status and list proxy/sync resource"),
Long: templates.LongDesc(i18n.T(`
Show connect status and list proxy/clone resource
Show connect status and list proxy/sync resource
Show connect status and list proxy or clone resource, you can check connect status by filed status and netif.
Show connect status and list proxy or sync resource, you can check connect status by filed status and netif.
if netif is empty, means tun device closed, so it's unhealthy, it will also show route info, if proxy workloads,
not only show myself proxy resource, another route info will also display.
`)),
Example: templates.Examples(i18n.T(`
# show status for connect status and list proxy/clone resource
# show status for connect status and list proxy/sync resource
kubevpn status
# query status by alias config name dev_new
@@ -65,27 +67,26 @@ func CmdStatus(f cmdutil.Factory) *cobra.Command {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
var clusterIDs []string
var connectionIDs []string
if aliasName != "" {
configs, err := ParseAndGet(localFile, remoteAddr, aliasName)
if err != nil {
return err
}
for _, config := range configs {
clusterID, err := GetClusterIDByConfig(cmd, config)
for _, conf := range configs {
connectionID, err := GetConnectionIDByConfig(cmd, conf)
if err != nil {
return err
}
clusterIDs = append(clusterIDs, clusterID)
connectionIDs = append(connectionIDs, connectionID)
}
}
resp, err := daemon.GetClient(false).Status(
cmd.Context(),
&rpc.StatusRequest{
ClusterIDs: clusterIDs,
},
)
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
resp, err := cli.Status(cmd.Context(), &rpc.StatusRequest{ConnectionIDs: connectionIDs})
if err != nil {
return err
}
@@ -98,9 +99,9 @@ func CmdStatus(f cmdutil.Factory) *cobra.Command {
},
}
cmd.Flags().StringVar(&aliasName, "alias", "", "Alias name, query connect status by alias config name")
cmd.Flags().StringVarP(&localFile, "kubevpnconfig", "f", util.If(os.Getenv("KUBEVPNCONFIG") != "", os.Getenv("KUBEVPNCONFIG"), config.GetConfigFilePath()), "Path to the kubevpnconfig file to use for CLI requests.")
cmd.Flags().StringVarP(&localFile, "kubevpnconfig", "f", util.If(os.Getenv("KUBEVPNCONFIG") != "", os.Getenv("KUBEVPNCONFIG"), config.GetConfigFile()), "Path to the kubevpnconfig file to use for CLI requests.")
cmd.Flags().StringVarP(&remoteAddr, "remote", "r", "", "Remote config file, eg: https://raw.githubusercontent.com/kubenetworks/kubevpn/master/pkg/config/config.yaml")
cmd.Flags().StringVarP(&format, "output", "o", FormatTable, fmt.Sprintf("Output format. One of: (%s, %s, %s)", FormatJson, FormatYaml, FormatTable))
cmd.Flags().StringVarP(&format, "output", "o", FormatTable, fmt.Sprintf("Output format. One of: (%s, %s, %s, %s)", FormatJson, FormatYaml, FormatTable, FormatTableWide))
return cmd
}
@@ -110,7 +111,7 @@ func genOutput(status *rpc.StatusResponse, format string) (string, error) {
if len(status.List) == 0 {
return "", nil
}
marshal, err := json.Marshal(status.List)
marshal, err := json.Marshal(status)
if err != nil {
return "", err
}
@@ -120,29 +121,68 @@ func genOutput(status *rpc.StatusResponse, format string) (string, error) {
if len(status.List) == 0 {
return "", nil
}
marshal, err := yaml.Marshal(status.List)
marshal, err := yaml.Marshal(status)
if err != nil {
return "", err
}
return string(marshal), nil
case FormatTableWide:
var sb = new(bytes.Buffer)
w := printers.GetNewTabWriter(sb)
genConnectWideMsg(w, status.CurrentConnectionID, status.List)
genProxyMsg(w, status.List)
genSyncMsg(w, status.List)
_ = w.Flush()
return sb.String(), nil
default:
var sb = new(bytes.Buffer)
w := printers.GetNewTabWriter(sb)
genConnectMsg(w, status.List)
genConnectMsg(w, status.CurrentConnectionID, status.List)
genProxyMsg(w, status.List)
genCloneMsg(w, status.List)
genSyncMsg(w, status.List)
_ = w.Flush()
return sb.String(), nil
}
}
func genConnectMsg(w *tabwriter.Writer, status []*rpc.Status) {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "ID", "Mode", "Cluster", "Kubeconfig", "Namespace", "Status", "Netif")
func genConnectMsg(w *tabwriter.Writer, currentConnectionID string, status []*rpc.Status) {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "CURRENT", "CONNECTION ID", "CLUSTER", "KUBECONFIG", "NAMESPACE", "STATUS", "NETIF")
for _, c := range status {
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\t%s\n", c.ID, c.Mode, c.Cluster, c.Kubeconfig, c.Namespace, c.Status, c.Netif)
current := util.If[string](c.ConnectionID == currentConnectionID, "*", "")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", current, c.ConnectionID, c.Cluster, shortenPath(c.Kubeconfig), c.Namespace, c.Status, c.Netif)
}
}
func genConnectWideMsg(w *tabwriter.Writer, currentConnectionID string, status []*rpc.Status) {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "CURRENT", "CONNECTION ID", "CLUSTER", "KUBECONFIG", "NAMESPACE", "STATUS", "NETIF", "IP")
for _, c := range status {
current := util.If[string](c.ConnectionID == currentConnectionID, "*", "")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", current, c.ConnectionID, c.Cluster, shortenPath(c.Kubeconfig), c.Namespace, c.Status, c.Netif, c.IPv4)
}
}
func shortenPath(absPath string) string {
// on windows
// cmd.exe not recognize '~', eg: cd ~, will error
// powershell.exe can recognize '~'
if util.IsWindows() {
return absPath
}
homeDir, err := os.UserHomeDir()
if err != nil {
return absPath
}
relativePath, err := filepath.Rel(homeDir, absPath)
if err != nil {
return absPath
}
if strings.HasPrefix(relativePath, "..") {
return absPath
}
return filepath.Join("~", relativePath)
}
func genProxyMsg(w *tabwriter.Writer, list []*rpc.Status) {
var needsPrint bool
for _, status := range list {
@@ -157,7 +197,7 @@ func genProxyMsg(w *tabwriter.Writer, list []*rpc.Status) {
_, _ = fmt.Fprintf(w, "\n")
w.SetRememberedWidths(nil)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", "ID", "Name", "Headers", "IP", "PortMap", "CurrentPC")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", strings.Repeat(" ", len("CURRENT")), "CONNECTION ID", "NAMESPACE", "NAME", "HEADERS", "PORTS", "CURRENT PC")
for _, c := range list {
for _, proxy := range c.ProxyList {
for _, rule := range proxy.RuleList {
@@ -172,11 +212,12 @@ func genProxyMsg(w *tabwriter.Writer, list []*rpc.Status) {
for k, v := range rule.PortMap {
portmap = append(portmap, fmt.Sprintf("%d->%d", k, v))
}
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%v\n",
c.ID,
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%v\n",
"",
c.ConnectionID,
proxy.Namespace,
proxy.Workload,
strings.Join(headers, ","),
rule.LocalTunIPv4,
strings.Join(portmap, ","),
rule.CurrentDevice,
)
@@ -185,10 +226,10 @@ func genProxyMsg(w *tabwriter.Writer, list []*rpc.Status) {
}
}
func genCloneMsg(w *tabwriter.Writer, list []*rpc.Status) {
func genSyncMsg(w *tabwriter.Writer, list []*rpc.Status) {
var needsPrint bool
for _, status := range list {
if len(status.CloneList) != 0 {
if len(status.SyncList) != 0 {
needsPrint = true
break
}
@@ -199,11 +240,11 @@ func genCloneMsg(w *tabwriter.Writer, list []*rpc.Status) {
_, _ = fmt.Fprintf(w, "\n")
w.SetRememberedWidths(nil)
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", "ID", "Name", "Headers", "ToName", "ToKubeconfig", "ToNamespace", "SyncthingGUI")
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", strings.Repeat(" ", len("CURRENT")), "CONNECTION ID", "NAMESPACE", "NAME", "HEADERS", "TO NAME", "SYNCTHING GUI")
for _, c := range list {
for _, clone := range c.CloneList {
//_, _ = fmt.Fprintf(w, "%s\n", clone.Workload)
for _, rule := range clone.RuleList {
for _, sync := range c.SyncList {
//_, _ = fmt.Fprintf(w, "%s\n", sync.Workload)
for _, rule := range sync.RuleList {
var headers []string
for k, v := range rule.Headers {
headers = append(headers, fmt.Sprintf("%s=%s", k, v))
@@ -211,30 +252,32 @@ func genCloneMsg(w *tabwriter.Writer, list []*rpc.Status) {
if len(headers) == 0 {
headers = []string{"*"}
}
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\t%s\n",
c.ID,
clone.Workload,
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
"",
c.ConnectionID,
sync.Namespace,
sync.Workload,
strings.Join(headers, ","),
rule.DstWorkload,
rule.DstKubeconfig,
rule.DstNamespace,
clone.SyncthingGUIAddr,
sync.SyncthingGUIAddr,
)
}
}
}
}
func GetClusterIDByConfig(cmd *cobra.Command, config Config) (string, error) {
func GetConnectionIDByConfig(cmd *cobra.Command, config Config) (string, error) {
flags := flag.NewFlagSet("", flag.ContinueOnError)
var sshConf = &pkgssh.SshConfig{}
pkgssh.AddSshFlags(flags, sshConf)
handler.AddExtraRoute(flags, &handler.ExtraRouteInfo{})
configFlags := genericclioptions.NewConfigFlags(false).WithDeprecatedPasswordFlag()
configFlags := genericclioptions.NewConfigFlags(true)
configFlags.AddFlags(flags)
matchVersionFlags := cmdutil.NewMatchVersionFlags(&warp{ConfigFlags: configFlags})
var kubeconfigJson string
AddKubeconfigJsonFlag(flags, &kubeconfigJson)
matchVersionFlags := cmdutil.NewMatchVersionFlags(&warp{ConfigFlags: configFlags, KubeconfigJson: kubeconfigJson})
matchVersionFlags.AddFlags(flags)
factory := cmdutil.NewFactory(matchVersionFlags)
f := cmdutil.NewFactory(matchVersionFlags)
for _, command := range cmd.Parent().Commands() {
command.Flags().VisitAll(func(f *flag.Flag) {
@@ -248,36 +291,81 @@ func GetClusterIDByConfig(cmd *cobra.Command, config Config) (string, error) {
_ = flags.Set(flag.Name, value)
return nil
})
bytes, ns, err := util.ConvertToKubeConfigBytes(factory)
kubeConfigBytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return "", err
}
file, err := util.ConvertToTempKubeconfigFile(bytes)
if err != nil {
return "", err
var file string
defer os.Remove(file)
if !sshConf.IsEmpty() {
file, err = pkgssh.SshJump(cmd.Context(), sshConf, kubeConfigBytes, false)
} else {
file, err = util.ConvertToTempKubeconfigFile(kubeConfigBytes, "")
}
flags = flag.NewFlagSet("", flag.ContinueOnError)
flags.AddFlag(&flag.Flag{
Name: "kubeconfig",
DefValue: file,
})
flags.AddFlag(&flag.Flag{
Name: "namespace",
DefValue: ns,
})
var path string
path, err = pkgssh.SshJump(cmd.Context(), sshConf, flags, false)
if err != nil {
return "", err
}
var c = &handler.ConnectOptions{}
err = c.InitClient(util.InitFactoryByPath(path, ns))
err = c.InitClient(util.InitFactoryByPath(file, ns))
if err != nil {
return "", err
}
err = c.InitDHCP(cmd.Context())
id, err := util.GetConnectionID(cmd.Context(), c.GetClientset().CoreV1().Namespaces(), ns)
if err != nil {
return "", err
}
return c.GetClusterID(), nil
return id, nil
}
func ParseArgs(cmd *cobra.Command, conf *Config) error {
var str string
for i := 0; i < len(conf.Flags); i++ {
kubeconfigJson, err := parseKubeconfigJson(cmd, []string{conf.Flags[i]})
if err != nil {
return err
}
if kubeconfigJson != "" {
str = kubeconfigJson
conf.Flags = append(conf.Flags[:i], conf.Flags[i+1:]...)
i--
}
}
if str == "" {
return nil
}
file, err := util.ConvertToTempKubeconfigFile([]byte(str), filepath.Join(config.GetTempPath(), conf.Name))
if err != nil {
return err
}
conf.Flags = append(conf.Flags, fmt.Sprintf("%s=%s", "--kubeconfig", file))
return nil
}
func parseKubeconfigJson(cmd *cobra.Command, args []string) (string, error) {
flags := flag.NewFlagSet("", flag.ContinueOnError)
var sshConf = &pkgssh.SshConfig{}
pkgssh.AddSshFlags(flags, sshConf)
handler.AddExtraRoute(flags, &handler.ExtraRouteInfo{})
configFlags := genericclioptions.NewConfigFlags(true)
configFlags.AddFlags(flags)
var kubeconfigJson string
AddKubeconfigJsonFlag(flags, &kubeconfigJson)
matchVersionFlags := cmdutil.NewMatchVersionFlags(&warp{ConfigFlags: configFlags})
matchVersionFlags.AddFlags(flags)
for _, command := range cmd.Parent().Commands() {
command.Flags().VisitAll(func(f *flag.Flag) {
if flags.Lookup(f.Name) == nil && flags.ShorthandLookup(f.Shorthand) == nil {
flags.AddFlag(f)
}
})
}
err := flags.ParseAll(args, func(flag *flag.Flag, value string) error {
_ = flags.Set(flag.Name, value)
return nil
})
return kubeconfigJson, err
}

View File

@@ -7,25 +7,23 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
)
func TestPrintProxyAndClone(t *testing.T) {
func TestPrintProxyAndSync(t *testing.T) {
var status = &rpc.StatusResponse{
List: []*rpc.Status{
{
ID: 0,
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun4",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{
{
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/authors",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/authors",
RuleList: []*rpc.ProxyRule{
{
Headers: map[string]string{"user": "naison"},
@@ -37,35 +35,29 @@ func TestPrintProxyAndClone(t *testing.T) {
},
},
},
CloneList: []*rpc.Clone{
SyncList: []*rpc.Sync{
{
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/ratings",
RuleList: []*rpc.CloneRule{{
Headers: map[string]string{"user": "naison"},
DstClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
DstCluster: "ccm6epn7qvcplhs3o8p00",
DstKubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
DstNamespace: "vke-system",
DstWorkload: "deployment.apps/ratings-clone-5ngn6",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/ratings",
RuleList: []*rpc.SyncRule{{
Headers: map[string]string{"user": "naison"},
DstWorkload: "deployment.apps/ratings-sync-5ngn6",
}},
},
},
},
{
ID: 1,
ClusterID: "c08cae70-0021-46c9-a1dc-38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{},
ConnectionID: "38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{},
},
},
}
@@ -80,21 +72,19 @@ func TestPrintProxy(t *testing.T) {
var status = &rpc.StatusResponse{
List: []*rpc.Status{
{
ID: 0,
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun4",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{
{
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/authors",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/authors",
RuleList: []*rpc.ProxyRule{
{
Headers: map[string]string{"user": "naison"},
@@ -106,19 +96,17 @@ func TestPrintProxy(t *testing.T) {
},
},
},
CloneList: []*rpc.Clone{},
SyncList: []*rpc.Sync{},
},
{
ID: 1,
ClusterID: "c08cae70-0021-46c9-a1dc-38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{},
ConnectionID: "38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{},
},
},
}
@@ -129,48 +117,40 @@ func TestPrintProxy(t *testing.T) {
fmt.Println(output)
}
func TestPrintClone(t *testing.T) {
func TestPrintSync(t *testing.T) {
var status = &rpc.StatusResponse{
List: []*rpc.Status{
{
ID: 0,
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{
{
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/ratings",
RuleList: []*rpc.CloneRule{{
Headers: map[string]string{"user": "naison"},
DstClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
DstCluster: "ccm6epn7qvcplhs3o8p00",
DstKubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
DstNamespace: "vke-system",
DstWorkload: "deployment.apps/ratings-clone-5ngn6",
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Workload: "deployment.apps/ratings",
RuleList: []*rpc.SyncRule{{
Headers: map[string]string{"user": "naison"},
DstWorkload: "deployment.apps/ratings-sync-5ngn6",
}},
},
},
},
{
ID: 1,
ClusterID: "c08cae70-0021-46c9-a1dc-38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{},
ConnectionID: "38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{},
},
},
}
@@ -185,28 +165,24 @@ func TestPrint(t *testing.T) {
var status = &rpc.StatusResponse{
List: []*rpc.Status{
{
ID: 0,
ClusterID: "ac6d8dfb-1d23-4f2a-b11e-9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{},
ConnectionID: "9c775fd22b84",
Cluster: "ccm6epn7qvcplhs3o8p00",
Kubeconfig: "/Users/bytedance/.kube/test-feiyan-config-private-new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun4",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{},
},
{
ID: 1,
ClusterID: "c08cae70-0021-46c9-a1dc-38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Mode: "full",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "Connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
CloneList: []*rpc.Clone{},
ConnectionID: "38e6a2f11443",
Cluster: "ccnepblsebp68ivej4a20",
Kubeconfig: "/Users/bytedance/.kube/dev_fy_config_new",
Namespace: "vke-system",
Status: "connected",
Netif: "utun5",
ProxyList: []*rpc.Proxy{},
SyncList: []*rpc.Sync{},
},
},
}

153
cmd/kubevpn/cmds/sync.go Normal file
View File

@@ -0,0 +1,153 @@
package cmds
import (
"context"
"fmt"
"os"
pkgerr "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
utilcomp "k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
"github.com/wencaiwulue/kubevpn/v2/pkg/handler"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
pkgssh "github.com/wencaiwulue/kubevpn/v2/pkg/ssh"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
"github.com/wencaiwulue/kubevpn/v2/pkg/util/regctl"
)
// CmdSync multiple cluster operate, can start up one deployment to another cluster
// kubectl exec POD_NAME -c CONTAINER_NAME /sbin/killall5 or ephemeralcontainers
func CmdSync(f cmdutil.Factory) *cobra.Command {
var options = handler.SyncOptions{}
var sshConf = &pkgssh.SshConfig{}
var extraRoute = &handler.ExtraRouteInfo{}
var transferImage bool
var syncDir string
var imagePullSecretName string
cmd := &cobra.Command{
Use: "sync",
Short: i18n.T("Sync local dir to cloned workloads dir"),
Long: templates.LongDesc(i18n.T(`
Sync local dir to cloned workloads which run in current namespace with same volume、env and network as target workloads
In this way, we startup another deployment in current namespace, but with different image version,
it also supports service mesh proxy. only traffic with special header will hit to sync resource.
`)),
Example: templates.Examples(i18n.T(`
# sync
- sync deployment run in current namespace with sync ~/code to /code/app
kubevpn sync deployment/productpage --sync ~/code:/code/app
# sync with mesh, traffic with header foo=bar, will hit sync workloads, otherwise hit origin workloads
kubevpn sync deployment/productpage --sync ~/code:/code/app --headers foo=bar
# sync workloads which api-server behind of bastion host or ssh jump host
kubevpn sync deployment/productpage --sync ~/code:/code/app --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem --headers foo=bar
# It also supports ProxyJump, like
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘
kubevpn sync deployment/productpage --sync ~/code:/code/app --ssh-alias <alias> --headers foo=bar
# Support ssh auth GSSAPI
kubevpn sync deployment/productpage --sync ~/code:/code/app --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-keytab /path/to/keytab
kubevpn sync deployment/productpage --sync ~/code:/code/app --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-cache /path/to/cache
kubevpn sync deployment/productpage --sync ~/code:/code/app --ssh-addr <HOST:PORT> --ssh-username <USERNAME> --gssapi-password <PASSWORD>
`)),
Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.MinimumNArgs(1)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
plog.InitLoggerForClient()
// startup daemon process and sudo process
err = daemon.StartupDaemon(cmd.Context())
if err != nil {
return err
}
if transferImage {
err = regctl.TransferImageWithRegctl(cmd.Context(), config.OriginImage, config.Image)
}
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if syncDir != "" {
local, remote, err := util.ParseDirMapping(syncDir)
if err != nil {
return pkgerr.Wrapf(err, "options 'sync' is invalid, %s", syncDir)
}
options.LocalDir = local
options.RemoteDir = remote
} else {
options.RemoteDir = config.DefaultRemoteDir
}
bytes, ns, err := util.ConvertToKubeConfigBytes(f)
if err != nil {
return err
}
if !sshConf.IsEmpty() {
if ip := util.GetAPIServerFromKubeConfigBytes(bytes); ip != nil {
extraRoute.ExtraCIDR = append(extraRoute.ExtraCIDR, ip.String())
}
}
req := &rpc.SyncRequest{
KubeconfigBytes: string(bytes),
Namespace: ns,
Headers: options.Headers,
Workloads: args,
ExtraRoute: extraRoute.ToRPC(),
OriginKubeconfigPath: util.GetKubeConfigPath(f),
SshJump: sshConf.ToRPC(),
TargetContainer: options.TargetContainer,
TargetImage: options.TargetImage,
TransferImage: transferImage,
Image: config.Image,
ImagePullSecretName: imagePullSecretName,
Level: int32(util.If(config.Debug, log.DebugLevel, log.InfoLevel)),
LocalDir: options.LocalDir,
RemoteDir: options.RemoteDir,
}
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
resp, err := cli.Sync(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.SyncResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
}
return err
}
_, _ = fmt.Fprintln(os.Stdout, config.Slogan)
return nil
},
}
cmd.Flags().StringToStringVarP(&options.Headers, "headers", "H", map[string]string{}, "Traffic with special headers (use `and` to match all headers) with reverse it to target cluster sync workloads, If not special, redirect all traffic to target cluster sync workloads. eg: --headers foo=bar --headers env=dev")
handler.AddCommonFlags(cmd.Flags(), &transferImage, &imagePullSecretName)
cmdutil.AddContainerVarFlags(cmd, &options.TargetContainer, options.TargetContainer)
cmd.Flags().StringVar(&options.TargetImage, "target-image", "", "Sync container use this image to startup container, if not special, use origin image")
cmd.Flags().StringVar(&syncDir, "sync", "", "Sync local dir to remote pod dir. format: LOCAL_DIR:REMOTE_DIR, eg: ~/code:/app/code")
handler.AddExtraRoute(cmd.Flags(), extraRoute)
pkgssh.AddSshFlags(cmd.Flags(), sshConf)
cmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
return cmd
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdSyncthing(_ cmdutil.Factory) *cobra.Command {
func CmdSyncthing(cmdutil.Factory) *cobra.Command {
var detach bool
var dir string
cmd := &cobra.Command{

View File

@@ -1,6 +1,8 @@
package cmds
import (
"context"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -58,16 +60,23 @@ func CmdUninstall(f cmdutil.Factory) *cobra.Command {
if err != nil {
return err
}
cli := daemon.GetClient(false)
disconnect, err := cli.Disconnect(cmd.Context(), &rpc.DisconnectRequest{
KubeconfigBytes: ptr.To(string(bytes)),
Namespace: ptr.To(ns),
SshJump: sshConf.ToRPC(),
})
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
disconnectResp, err := cli.Disconnect(context.Background())
if err != nil {
plog.G(cmd.Context()).Warnf("Failed to disconnect from cluter: %v", err)
} else {
_ = util.PrintGRPCStream[rpc.DisconnectResponse](disconnect)
err = disconnectResp.Send(&rpc.DisconnectRequest{
KubeconfigBytes: ptr.To(string(bytes)),
Namespace: ptr.To(ns),
SshJump: sshConf.ToRPC(),
})
if err != nil {
plog.G(cmd.Context()).Warnf("Failed to disconnect from cluter: %v", err)
}
_ = util.PrintGRPCStream[rpc.DisconnectResponse](cmd.Context(), disconnectResp)
}
req := &rpc.UninstallRequest{
@@ -75,11 +84,15 @@ func CmdUninstall(f cmdutil.Factory) *cobra.Command {
Namespace: ns,
SshJump: sshConf.ToRPC(),
}
resp, err := cli.Uninstall(cmd.Context(), req)
resp, err := cli.Uninstall(context.Background())
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.UninstallResponse](resp)
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.UninstallResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil

View File

@@ -0,0 +1,63 @@
package cmds
import (
"context"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon"
"github.com/wencaiwulue/kubevpn/v2/pkg/daemon/rpc"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func CmdUnsync(f cmdutil.Factory) *cobra.Command {
var cmd = &cobra.Command{
Use: "unsync",
Short: "unsync target resource",
Long: templates.LongDesc(i18n.T(`
Remove sync resource
This command is design to remove sync resources, after use command 'kubevpn sync xxx',
it will generate and create a new resource in target k8s cluster with format [resource_name]_sync_xxxxx,
use this command to remove this created resources.
`)),
Example: templates.Examples(i18n.T(`
# leave proxy resources to origin
kubevpn unsync deployment/authors-sync-645d7
`)),
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
return daemon.StartupDaemon(cmd.Context())
},
RunE: func(cmd *cobra.Command, args []string) error {
cli, err := daemon.GetClient(false)
if err != nil {
return err
}
req := &rpc.UnsyncRequest{
Workloads: args,
}
resp, err := cli.Unsync(context.Background())
if err != nil {
return err
}
err = resp.Send(req)
if err != nil {
return err
}
err = util.PrintGRPCStream[rpc.UnsyncResponse](cmd.Context(), resp)
if err != nil {
if status.Code(err) == codes.Canceled {
return nil
}
return err
}
return nil
},
}
return cmd
}

View File

@@ -1,58 +1,27 @@
package cmds
import (
"fmt"
"net/http"
"os"
"github.com/spf13/cobra"
"golang.org/x/oauth2"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/upgrade"
)
func CmdUpgrade(_ cmdutil.Factory) *cobra.Command {
func CmdUpgrade(cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "upgrade",
Short: i18n.T("Upgrade kubevpn client to latest version"),
Long: templates.LongDesc(i18n.T(`
Upgrade kubevpn client to latest version, automatically download and install latest kubevpn from GitHub.
disconnect all from k8s cluster, leave all resources, remove all clone resource, and then,
disconnect all from k8s cluster, leave all resources, remove all sync resource, and then,
upgrade local daemon grpc server to latest version.
`)),
RunE: func(cmd *cobra.Command, args []string) error {
const (
envLatestUrl = "KUBEVPN_LATEST_VERSION_URL"
)
plog.InitLoggerForClient()
var client = http.DefaultClient
if config.GitHubOAuthToken != "" {
client = oauth2.NewClient(cmd.Context(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: config.GitHubOAuthToken, TokenType: "Bearer"}))
}
var url = os.Getenv(envLatestUrl)
if url == "" {
var latestVersion string
var needsUpgrade bool
var err error
url, latestVersion, needsUpgrade, err = upgrade.NeedsUpgrade(cmd.Context(), client, config.Version)
if err != nil {
return err
}
if !needsUpgrade {
_, _ = fmt.Fprintln(os.Stdout, fmt.Sprintf("Already up to date, don't needs to upgrade, version: %s", latestVersion))
return nil
}
_, _ = fmt.Fprintln(os.Stdout, fmt.Sprintf("Current version is: %s less than latest version: %s, needs to upgrade", config.Version, latestVersion))
_ = os.Setenv(envLatestUrl, url)
_ = quit(cmd.Context(), false)
_ = quit(cmd.Context(), true)
}
return upgrade.Main(cmd.Context(), client, url)
return upgrade.Main(cmd.Context(), quit)
},
}
return cmd

View File

@@ -64,12 +64,13 @@ func init() {
}
func getDaemonVersion() string {
cli := daemon.GetClient(false)
if cli != nil {
version, err := cli.Version(context.Background(), &rpc.VersionRequest{})
if err == nil {
return version.Version
}
cli, err := daemon.GetClient(false)
if err != nil {
return "unknown"
}
return "unknown"
version, err := cli.Version(context.Background(), &rpc.VersionRequest{})
if err != nil {
return "unknown"
}
return version.Version
}

View File

@@ -1,11 +1,16 @@
package cmds
import (
"fmt"
"os"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/dhcp"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
"github.com/wencaiwulue/kubevpn/v2/pkg/webhook"
)
@@ -25,7 +30,16 @@ func CmdWebhook(f cmdutil.Factory) *cobra.Command {
go util.StartupPProfForServer(0)
},
RunE: func(cmd *cobra.Command, args []string) error {
return webhook.Main(f)
ns := os.Getenv(config.EnvPodNamespace)
if ns == "" {
return fmt.Errorf("failed to get pod namespace")
}
clientset, err := f.KubernetesClientSet()
if err != nil {
return err
}
manager := dhcp.NewDHCPManager(clientset, ns)
return webhook.Main(manager)
},
}
return cmd

View File

@@ -1,10 +1,11 @@
package main
import (
_ "net/http/pprof"
ctrl "sigs.k8s.io/controller-runtime"
_ "k8s.io/client-go/plugin/pkg/client/auth"
_ "net/http/pprof"
"github.com/wencaiwulue/kubevpn/v2/cmd/kubevpn/cmds"
)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 372 KiB

After

Width:  |  Height:  |  Size: 372 KiB

266
go.mod
View File

@@ -3,220 +3,202 @@ module github.com/wencaiwulue/kubevpn/v2
go 1.23.2
require (
github.com/calmh/incontainer v1.0.0
github.com/cilium/ipam v0.0.0-20230509084518-fd66eae7909b
github.com/containerd/containerd v1.7.14
github.com/containerd/containerd v1.7.27
github.com/containernetworking/cni v1.1.2
github.com/coredns/caddy v1.1.1
github.com/coredns/coredns v1.11.2
github.com/distribution/reference v0.6.0
github.com/docker/cli v27.5.1+incompatible
github.com/docker/docker v27.5.1+incompatible
github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0
github.com/docker/libcontainer v2.2.1+incompatible
github.com/envoyproxy/go-control-plane v0.13.1
github.com/fsnotify/fsnotify v1.7.0
github.com/envoyproxy/go-control-plane v0.13.4
github.com/envoyproxy/go-control-plane/envoy v1.35.0
github.com/fsnotify/fsnotify v1.8.0
github.com/gliderlabs/ssh v0.3.8
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
github.com/google/gopacket v1.1.19
github.com/google/uuid v1.6.0
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/go-version v1.7.0
github.com/hpcloud/tail v1.0.0
github.com/jcmturner/gofork v1.7.6
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/joho/godotenv v1.5.1
github.com/kevinburke/ssh_config v1.2.0
github.com/libp2p/go-netroute v0.2.1
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
github.com/miekg/dns v1.1.58
github.com/moby/term v0.5.0
github.com/opencontainers/image-spec v1.1.0
github.com/miekg/dns v1.1.64
github.com/moby/term v0.5.2
github.com/opencontainers/image-spec v1.1.1
github.com/pkg/errors v0.9.1
github.com/prometheus-community/pro-bing v0.4.0
github.com/regclient/regclient v0.8.0
github.com/schollz/progressbar/v3 v3.14.2
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/syncthing/syncthing v1.29.2
github.com/thejerf/suture/v4 v4.0.6
github.com/vishvananda/netlink v1.3.1
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.33.0
golang.org/x/net v0.34.0
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.11.0
golang.org/x/sys v0.30.0
golang.org/x/term v0.29.0
golang.org/x/text v0.22.0
golang.org/x/time v0.8.0
golang.org/x/crypto v0.38.0
golang.org/x/net v0.40.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sys v0.35.0
golang.org/x/term v0.32.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.69.4
google.golang.org/protobuf v1.36.3
google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987
k8s.io/api v0.31.0-alpha.0
k8s.io/apimachinery v0.31.0-alpha.0
k8s.io/cli-runtime v0.29.3
k8s.io/client-go v0.31.0-alpha.0
helm.sh/helm/v4 v4.0.0-20250324191910-0199b748aaea
k8s.io/api v0.32.3
k8s.io/apimachinery v0.32.3
k8s.io/cli-runtime v0.32.3
k8s.io/client-go v0.32.3
k8s.io/klog/v2 v2.130.1
k8s.io/kubectl v0.29.3
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
sigs.k8s.io/controller-runtime v0.18.4
sigs.k8s.io/kustomize/api v0.16.0
k8s.io/kubectl v0.32.3
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e
sigs.k8s.io/controller-runtime v0.20.4
sigs.k8s.io/kustomize/api v0.19.0
sigs.k8s.io/yaml v1.4.0
tailscale.com v1.74.1
)
require (
cel.dev/expr v0.16.2 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
dario.cat/mergo v1.0.0 // indirect
cel.dev/expr v0.23.0 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/DataDog/appsec-internal-go v1.5.0 // indirect
github.com/DataDog/datadog-agent/pkg/obfuscate v0.52.0 // indirect
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.52.0 // indirect
github.com/DataDog/datadog-go/v5 v5.5.0 // indirect
github.com/DataDog/go-libddwaf/v2 v2.4.2 // indirect
github.com/DataDog/go-sqllexer v0.0.11 // indirect
github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect
github.com/DataDog/sketches-go v1.4.4 // indirect
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.9 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/antonmedv/expr v1.15.5 // indirect
github.com/apparentlymart/go-cidr v1.1.0 // indirect
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/calmh/incontainer v1.0.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/calmh/xdr v1.2.0 // indirect
github.com/ccding/go-stun v0.1.5 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/chmduquesne/rollinghash v4.0.0+incompatible // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/cilium/ebpf v0.16.0 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dnstap/golang-dnstap v0.4.0 // indirect
github.com/docker/cli-docs-tool v0.9.0 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-events v0.0.0-20250114142523-c867878c5e32 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/ebitengine/purego v0.8.3 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/farsightsec/golang-framestream v0.3.0 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gaissmai/bart v0.11.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
github.com/go-ldap/ldap/v3 v3.4.10 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/greatroar/blobloom v0.8.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/illarion/gonotify/v2 v2.0.3 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/infobloxopen/go-trees v0.0.0-20221216143356-66ceba885ebc // indirect
github.com/jackpal/gateway v1.0.16 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jinzhu/gorm v1.9.16 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/lithammer/dedent v1.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/symlink v0.2.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
@@ -225,41 +207,35 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/oschwald/geoip2-golang v1.11.0 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/outcaste-io/ristretto v0.2.3 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/quic-go v0.49.0 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.16.0 // indirect
github.com/quic-go/quic-go v0.50.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rubenv/sql-migrate v1.7.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/tinylib/msgp v1.1.9 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/veraison/go-cose v1.3.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
@@ -267,48 +243,42 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/etcd/api/v3 v3.5.13 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect
go.etcd.io/etcd/client/v3 v3.5.13 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.starlark.net v0.0.0-20240329153429-e6e8e7ce1b7a // indirect
go.uber.org/atomic v1.11.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/api v0.172.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.62.0 // indirect
gopkg.in/evanphx/json-patch.v5 v5.9.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.30.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.31.0-alpha.0 // indirect
k8s.io/component-base v0.31.0-alpha.0 // indirect
k8s.io/kube-openapi v0.0.0-20240322212309-b815d8309940 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/kyaml v0.16.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/apiextensions-apiserver v0.32.3 // indirect
k8s.io/apiserver v0.32.3 // indirect
k8s.io/component-base v0.32.3 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
oras.land/oras-go/v2 v2.5.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
)

690
go.sum

File diff suppressed because it is too large Load Diff

6
go.work Normal file
View File

@@ -0,0 +1,6 @@
go 1.23.2
use (
.
./pkg/syncthing/auto
)

815
go.work.sum Normal file
View File

@@ -0,0 +1,815 @@
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg=
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/Antonboom/errname v0.1.9/go.mod h1:nLTcJzevREuAsgTbG85UsuiWpMpAqbKD1HNZ29OzE58=
github.com/Antonboom/nilnil v0.1.4/go.mod h1:iOov/7gRcXkeEU+EMGpBu2ORih3iyVEiWjeste1SJm8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/vcs v1.13.3/go.mod h1:TiE7xuEjl1N4j016moRd6vezp6e6Lz23gypeXfzXeW8=
github.com/Microsoft/cosesign1go v1.2.0/go.mod h1:1La/HcGw19rRLhPW0S6u55K6LKfti+GQSgGCtrfhVe8=
github.com/Microsoft/didx509go v0.0.3/go.mod h1:wWt+iQsLzn3011+VfESzznLIp/Owhuj7rLF7yLglYbk=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/kong v1.6.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/ashanbrown/forbidigo v1.5.1/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU=
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.64/go.mod h1:4Q7R9MFpXRdjO3YnAfUTdnuENs32WzBkASt6VxSYDYQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.25/go.mod h1:SUbB4wcbSEyCvqBxv/O/IBf93RbEze7U7OnoTlpPB+g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.28/go.mod h1:spfrICMD6wCAhjhzHuy6DOZZ+LAIY10UxhUmLzpJTTs=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.2/go.mod h1:4tfW5l4IAB32VWCDEBxCRtR9T4BWy4I4kr1spr8NgZM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.33.0/go.mod h1:J9kLNzEiHSeGMyN7238EjJmBpCniVzFda75Gxl/NqB8=
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.7/go.mod h1:Q7XIWsMo0JcMpI/6TGD6XXcXcV1DbTj6e9BKNntIMIM=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/bazelbuild/rules_go v0.44.2/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo=
github.com/bramvdbogaerde/go-scp v1.4.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ=
github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s=
github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U=
github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
github.com/containerd/btrfs/v2 v2.0.0/go.mod h1:swkD/7j9HApWpzl8OHfrHNxppPd9l44DFZdF94BUj9k=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/containerd/go-cni v1.1.9/go.mod h1:XYrZJ1d5W6E2VOvjffL3IZq0Dz6bsVlERHbekNK90PM=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/imgcrypt v1.1.8/go.mod h1:x6QvFIkMyO2qGIY2zXc88ivEzcbgvLdWjoZyGqDap5U=
github.com/containerd/nri v0.8.0/go.mod h1:uSkgBrCdEtAiEz4vnrq8gmAC4EnVAM5Klt0OuK5rZYQ=
github.com/containerd/protobuild v0.3.0/go.mod h1:5mNMFKKAwCIAkFBPiOdtRx2KiQlyEJeMXnL5R1DsWu8=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
github.com/containerd/zfs v1.1.0/go.mod h1:oZF9wBnrnQjpWLaPKEinrx3TQ9a+W/RJO7Zb41d8YLE=
github.com/containernetworking/plugins v1.2.0/go.mod h1:/VjX4uHecW5vVimFa1wkG4s+r/s9qIfPdqlLF4TW8c4=
github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4HUmreluQcMd8=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI=
github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps=
github.com/dave/astrid v0.0.0-20170323122508-8c2895878b14/go.mod h1:Sth2QfxfATb/nW4EsrSi2KyJmbcniZ8TgTaji17D6ms=
github.com/dave/brenda v1.1.0/go.mod h1:4wCUr6gSlu5/1Tk7akE5X7UorwiQ8Rij0SKH3/BGMOM=
github.com/dave/courtney v0.4.0/go.mod h1:3WSU3yaloZXYAxRuWt8oRyVb9SaRiMBt5Kz/2J227tM=
github.com/dave/patsy v0.0.0-20210517141501-957256f50cba/go.mod h1:qfR88CgEGLoiqDaE+xxDCi5QA5v4vUoW0UCX2Nd5Tlc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/elastic/crd-ref-docs v0.0.12/go.mod h1:X83mMBdJt05heJUYiS3T0yJ/JkCuliuhSUNav5Gjo/U=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
github.com/evanw/esbuild v0.19.11/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-critic/go-critic v0.8.0/go.mod h1:5TjdkPI9cu/yKbYS96BTsslihjKd6zg6vd8O9RZXj2s=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw=
github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ=
github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4=
github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA=
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/godror/godror v0.40.4/go.mod h1:i8YtVTHUJKfFT3wTat4A9UoqScUtZXiYB9Rf3SVARgc=
github.com/godror/knownpb v0.1.1/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs=
github.com/golangci/golangci-lint v1.52.2/go.mod h1:S5fhC5sHM5kE22/HcATKd1XLWQxX+y7mHj8B5H91Q/0=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-github/v56 v56.0.0/go.mod h1:D8cdcX98YWJvi7TLo7zM4/h8ZTx6u6fwGEkCdisopo0=
github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI=
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
github.com/goreleaser/chglog v0.5.0/go.mod h1:Ri46M3lrMuv76FHszs3vtABR8J8k1w9JHYAzxeeOl28=
github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
github.com/goreleaser/nfpm/v2 v2.33.1/go.mod h1:8wwWWvJWmn84xo/Sqiv0aMvEGTHlHZTXTEuVSgQpkIM=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/hanwen/go-fuse/v2 v2.3.0/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inetaf/tcpproxy v0.0.0-20240214030015-3ce58045626c/go.mod h1:Di7LXRyUcnvAcLicFhtM9/MlZl/TNgRSDHORM2c6CMI=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/intel/goresctrl v0.5.0/go.mod h1:mIe63ggylWYr0cU/l8n11FAkesqfvuP3oktIsxvu0T0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M=
github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes=
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4=
github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/lyft/protoc-gen-star/v2 v2.0.4-0.20230330145011-496ad1ac90a4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
github.com/maruel/panicparse/v2 v2.4.0/go.mod h1:nOY2OKe8csO3F3SA5+hsxot05JLgukrF54B9x88fVp4=
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I=
github.com/maxmind/geoipupdate/v6 v6.1.0/go.mod h1:cZYCDzfMzTY4v6dKRdV7KTB6SStxtn3yFkiJ1btTGGc=
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
github.com/mgechev/revive v1.3.1/go.mod h1:YlD6TTWl2B8A103R9KWJSPVI9DrEf+oqr15q21Ld+5I=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/nelsam/hel/v2 v2.3.3/go.mod h1:1ZTGfU2PFTOd5mx22i5O0Lc2GY933lQ2wb/ggy+rL3w=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nishanths/exhaustive v0.10.0/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA=
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
github.com/nunnatsa/ginkgolinter v0.11.2/go.mod h1:dJIGXYXbkBswqa/pIzG0QlVTTDSBMxDoCFwhsl4Uras=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w=
github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
github.com/polyfloyd/go-errorlint v1.4.1/go.mod h1:k6fU/+fQe38ednoZS51T7gSIGQW1y94d6TkSr35OzH8=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/prometheus v0.49.2-0.20240125131847-c3b8ef1694ff/go.mod h1:FvE8dtQ1Ww63IlyKBn1V4s+zMwF9kHkVNkQBR1pM4CU=
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw=
github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50=
github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU=
github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY=
github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
github.com/tailscale/golang-x-crypto v0.0.0-20240604161659-3fde5e568aa4/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/tailscale/mkctr v0.0.0-20240628074852-17ca944da6ba/go.mod h1:DxnqIXBplij66U2ZkL688xy07q97qQ83P+TVueLiHq4=
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4/go.mod h1:phI29ccmHQBc+wvroosENp1IF9195449VDnFDhJ4rJU=
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ=
github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE=
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/u-root/u-root v0.12.0/go.mod h1:FYjTOh4IkIZHhjsd17lb8nYW6udgXdJhG1c0r6u0arI=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/veraison/go-cose v1.1.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4=
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE=
go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY=
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8=
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
k8s.io/code-generator v0.32.3/go.mod h1:+mbiYID5NLsBuqxjQTygKM/DAdKpAjvBzrJd64NU1G8=
k8s.io/component-helpers v0.32.3/go.mod h1:utTBXk8lhkJewBKNuNf32Xl3KT/0VV19DmiXU/SV4Ao=
k8s.io/cri-api v0.27.1/go.mod h1:+Ts/AVYbIo04S86XbTD73UPp/DkTiYxtsFeOFEu32L0=
k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM=
k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU=
mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8/go.mod h1:Oh/d7dEtzsNHGOq1Cdv8aMm3KdKhVvPbRQcM8WFpBR8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
sigs.k8s.io/controller-tools v0.15.1-0.20240618033008-7824932b0cab/go.mod h1:egedX5jq2KrZ3A2zaOz3e2DSsh5BhFyyjvNcBRIQel8=
sigs.k8s.io/kustomize/kustomize/v5 v5.5.0/go.mod h1:AeFCmgCrXzmvjWWaeZCyBp6XzG1Y0w1svYus8GhJEOE=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
tags.cncf.io/container-device-interface v0.8.1/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=

View File

@@ -2,8 +2,6 @@ package config
import (
"net"
"os"
"path/filepath"
"sync"
"time"
@@ -14,6 +12,12 @@ const (
// configmap name
ConfigMapPodTrafficManager = "kubevpn-traffic-manager"
// helm app name kubevpn
HelmAppNameKubevpn = "kubevpn"
// default installed namespace
DefaultNamespaceKubevpn = "kubevpn"
// config map keys
KeyDHCP = "DHCP"
KeyDHCP6 = "DHCP6"
@@ -25,40 +29,41 @@ const (
TLSCertKey = "tls_crt"
// TLSPrivateKeyKey is the key for the private key field in a TLS secret.
TLSPrivateKeyKey = "tls_key"
// TLSServerName for tls config server name
TLSServerName = "tls_server_name"
// container name
ContainerSidecarEnvoyProxy = "envoy-proxy"
ContainerSidecarControlPlane = "control-plane"
ContainerSidecarWebhook = "webhook"
ContainerSidecarVPN = "vpn"
ContainerSidecarSyncthing = "syncthing"
VolumeEnvoyConfig = "envoy-config"
VolumeSyncthing = "syncthing"
VolumeSyncthing = "syncthing"
// innerIPv4Pool is used as tun ip
// 198.19.0.0/16 network is part of the 198.18.0.0/15 (reserved for benchmarking).
// IPv4Pool is used as tun ip
// 198.19.0.0/16 network is part of the 198.18.0.0/15 (reserved for benchmarking).
// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
innerIPv4Pool = "198.19.0.100/16"
// 原因在docker环境中设置docker的 gateway 和 subnet不能 inner 的冲突,也不能和 docker的 172.17 冲突
// 不然的话,请求会不通的
// 解决的问题:在 k8s 中的 名叫 kubernetes 的 service ip 为
// ➜ ~ kubectl get service kubernetes
//NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
//kubernetes ClusterIP 172.17.0.1 <none> 443/TCP 190d
//
// ➜ ~ docker network inspect bridge | jq '.[0].IPAM.Config'
//[
// {
// "Subnet": "172.17.0.0/16",
// "Gateway": "172.17.0.1"
// }
//]
// 如果不创建 network那么是无法请求到 这个 kubernetes 的 service 的
dockerInnerIPv4Pool = "198.18.0.100/16"
// so we split it into 2 parts: 198.18.0.0/15 --> [198.19.0.0/16, 198.19.0.0/16]
IPv4Pool = "198.19.0.0/16"
// 2001:2::/64 network is part of the 2001:2::/48 (reserved for benchmarking)
// https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
innerIPv6Pool = "2001:2::9999/64"
IPv6Pool = "2001:2::/64"
/*
reasondocker use 172.17.0.0/16 network conflict with k8s service kubernetes
➜ ~ kubectl get service kubernetes
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.17.0.1 <none> 443/TCP 190d
➜ ~ docker network inspect bridge | jq '.[0].IPAM.Config'
[
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
*/
DockerIPv4Pool = "198.18.0.1/16"
DefaultNetDir = "/etc/cni/net.d"
@@ -88,11 +93,9 @@ const (
EnvSSHJump = "SSH_JUMP_BY_KUBEVPN"
// hosts entry key word
HostsKeyWord = "# Add by KubeVPN"
GHCR_IMAGE_REGISTRY = "ghcr.io"
DOCKER_IMAGE_REGISTRY = "docker.io"
// hosts entry keyword
HostsKeyword = "Added by KubeVPN"
HostsDeviceKeyword = "# For dev %s " + HostsKeyword
)
var (
@@ -105,10 +108,6 @@ var (
GitHubOAuthToken = ""
OriginImage = "ghcr.io/kubenetworks/kubevpn:" + Version
DaemonPath string
HomePath string
PprofPath string
)
var (
@@ -124,25 +123,18 @@ var (
func init() {
var err error
RouterIP, CIDR, err = net.ParseCIDR(innerIPv4Pool)
RouterIP, CIDR, err = net.ParseCIDR(IPv4Pool)
if err != nil {
panic(err)
}
RouterIP6, CIDR6, err = net.ParseCIDR(innerIPv6Pool)
RouterIP6, CIDR6, err = net.ParseCIDR(IPv6Pool)
if err != nil {
panic(err)
}
DockerRouterIP, DockerCIDR, err = net.ParseCIDR(dockerInnerIPv4Pool)
DockerRouterIP, DockerCIDR, err = net.ParseCIDR(DockerIPv4Pool)
if err != nil {
panic(err)
}
dir, err := os.UserHomeDir()
if err != nil {
panic(err)
}
DaemonPath = filepath.Join(dir, HOME, Daemon)
HomePath = filepath.Join(dir, HOME)
PprofPath = filepath.Join(dir, HOME, Daemon, PProfDir)
}
var Debug bool
@@ -154,7 +146,7 @@ var (
)
var (
KeepAliveTime = 180 * time.Second
KeepAliveTime = 60 * time.Second
DialTimeout = 15 * time.Second
HandshakeTimeout = 5 * time.Second
ConnectTimeout = 5 * time.Second
@@ -163,10 +155,47 @@ var (
)
var (
// network layer ip needs 20 bytes
// transport layer UDP header needs 8 bytes
// UDP over TCP header needs 22 bytes
DefaultMTU = 1500 - 20 - 8 - 21
// DefaultMTU
/**
+--------------------------------------------------------------------+
| Original IP Packet from TUN |
+-------------------+------------------------------------------------+
| IP Header (20B) | Payload (MTU size) |
+-------------------+------------------------------------------------+
After adding custom 2-byte header:
+----+-------------------+-------------------------------------------+
| LH | IP Header (20B) | Payload |
+----+-------------------+-------------------------------------------+
| 2B | 20B | 1453 - 20 = 1433B |
+----+-------------------+-------------------------------------------+
TLS 1.3 Record Structure Breakdown:
+---------------------+--------------------------+-------------------+
| TLS Header (5B) | Encrypted Data (N) | Auth Tag (16B) |
+---------------------+--------------------------+-------------------+
| Content Type (1) | ↑ | AEAD Authentication
| Version (2) | Encrypted Payload | (e.g. AES-GCM) |
| Length (2) | (Original Data + LH2) | |
+---------------------+--------------------------+-------------------+
|←------- 5B --------→|←---- Length Field ------→|←----- 16B -------→|
Final Ethernet Frame:
+--------+----------------+----------------+-----------------------+--------+
| EthHdr | IP Header | TCP Header | TLS Components |
| (14B) | (20B) | (20B) +---------+-------------+--------+
| | | | Hdr(5B) | Data+LH2 | Tag(16)|
+--------+----------------+----------------+---------+-------------+--------+
|←------------------- Total 1500B Ethernet Frame --------------------------→|
ipv4: 20
ipv6: 40
mtu = 1500 - ip header(20/40 v4/v6) - tcp header (20) - tls1.3(5+1+16) - packet over tcp(length(2)+remark(1)) = 1415
*/
DefaultMTU = 1500 - max(20, 40) - 20 - (5 + 1 + 16) - (2 + 1)
)
var (
@@ -187,11 +216,4 @@ var (
}
)
type Engine string
const (
EngineGvisor Engine = "gvisor"
EngineSystem Engine = "system"
)
const Slogan = "Now you can access resources in the kubernetes cluster !"

View File

@@ -1,19 +1,20 @@
# Here is an example config kubevpn config file, please change it into your custom config.
# Support three filed: Name,Needs,Flags
# Exec command: kubevpn alias qa <===> kubevpn connect --kubeconfig=~/.kube/jumper_config --namespace=default
# Simple is Good ~
# Just keep simple
Name: dev
Description: This is dev k8s environment, needs jump by qa env
Needs: qa
Flags:
- connect
- --kubeconfig=~/.kube/config
- --namespace=default
- --lite
---
Name: qa
Description: This is QA k8s environment
Flags:
- connect
- --kubeconfig=~/.kube/jumper_config

View File

@@ -11,40 +11,60 @@ import (
const (
HOME = ".kubevpn"
Daemon = "daemon"
Log = "log"
SockPath = "daemon.sock"
SudoSockPath = "sudo_daemon.sock"
SockPath = "user_daemon.sock"
SudoSockPath = "root_daemon.sock"
PidPath = "daemon.pid"
SudoPidPath = "sudo_daemon.pid"
PidPath = "user_daemon.pid"
SudoPidPath = "root_daemon.pid"
LogFile = "daemon.log"
UserLogFile = "user_daemon.log"
SudoLogFile = "root_daemon.log"
ConfigFile = "config.yaml"
TempDir = "temp"
DBFile = "db"
)
//go:embed config.yaml
var config []byte
var (
homePath string
daemonPath string
logPath string
//go:embed config.yaml
config []byte
)
func init() {
err := os.MkdirAll(DaemonPath, 0755)
dir, err := os.UserHomeDir()
if err != nil {
panic(err)
}
err = os.Chmod(DaemonPath, 0755)
if err != nil {
panic(err)
}
err = os.MkdirAll(PprofPath, 0755)
if err != nil {
panic(err)
}
err = os.Chmod(PprofPath, 0755)
if err != nil {
panic(err)
homePath = filepath.Join(dir, HOME)
daemonPath = filepath.Join(dir, HOME, Daemon)
logPath = filepath.Join(dir, HOME, Log)
var paths = []string{homePath, daemonPath, logPath, GetPProfPath(), GetSyncthingPath(), GetTempPath()}
for _, path := range paths {
_, err = os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
err = os.MkdirAll(path, 0755)
if err != nil {
panic(err)
}
err = os.Chmod(path, 0755)
if err != nil {
panic(err)
}
} else if err != nil {
panic(err)
}
}
path := filepath.Join(HomePath, ConfigFile)
path := filepath.Join(homePath, ConfigFile)
_, err = os.Stat(path)
if errors.Is(err, os.ErrNotExist) {
err = os.WriteFile(path, config, 0644)
@@ -59,7 +79,7 @@ func GetSockPath(isSudo bool) string {
if isSudo {
name = SudoSockPath
}
return filepath.Join(DaemonPath, name)
return filepath.Join(daemonPath, name)
}
func GetPidPath(isSudo bool) string {
@@ -67,17 +87,32 @@ func GetPidPath(isSudo bool) string {
if isSudo {
name = SudoPidPath
}
return filepath.Join(DaemonPath, name)
return filepath.Join(daemonPath, name)
}
func GetSyncthingPath() string {
return filepath.Join(DaemonPath, SyncthingDir)
return filepath.Join(daemonPath, SyncthingDir)
}
func GetSyncthingGUIPath() string {
return filepath.Join(DaemonPath, SyncthingDir, SyncthingGUIDir)
func GetConfigFile() string {
return filepath.Join(homePath, ConfigFile)
}
func GetConfigFilePath() string {
return filepath.Join(HomePath, ConfigFile)
func GetTempPath() string {
return filepath.Join(homePath, TempDir)
}
func GetDaemonLogPath(isSudo bool) string {
if isSudo {
return filepath.Join(logPath, SudoLogFile)
}
return filepath.Join(logPath, UserLogFile)
}
func GetPProfPath() string {
return filepath.Join(daemonPath, PProfDir)
}
func GetDBPath() string {
return filepath.Join(daemonPath, DBFile)
}

View File

@@ -9,13 +9,8 @@ import (
const (
SyncthingDir = "syncthing"
SyncthingGUIDir = "gui"
DefaultRemoteDir = "/kubevpn-data"
// EnvDisableSyncthingLog disable syncthing log, because it can not set output writer, only write os.Stdout or io.Discard
EnvDisableSyncthingLog = "LOGGER_DISCARD"
SyncthingAPIKey = "kubevpn"
)

View File

@@ -38,9 +38,10 @@ import (
)
type Virtual struct {
Uid string // group.resource.name
Ports []ContainerPort
Rules []*Rule
Namespace string
Uid string // group.resource.name
Ports []ContainerPort
Rules []*Rule
}
type ContainerPort struct {
@@ -90,7 +91,7 @@ type Rule struct {
PortMap map[int32]string
}
func (a *Virtual) To(enableIPv6 bool, logger *log.Logger) (
func (a *Virtual) To(enableIPv6 bool, logger *log.Entry) (
listeners []types.Resource,
clusters []types.Resource,
routes []types.Resource,
@@ -100,7 +101,7 @@ func (a *Virtual) To(enableIPv6 bool, logger *log.Logger) (
for _, port := range a.Ports {
isFargateMode := port.EnvoyListenerPort != 0
listenerName := fmt.Sprintf("%s_%v_%s", a.Uid, util.If(isFargateMode, port.EnvoyListenerPort, port.ContainerPort), port.Protocol)
listenerName := fmt.Sprintf("%s_%s_%v_%s", a.Namespace, a.Uid, util.If(isFargateMode, port.EnvoyListenerPort, port.ContainerPort), port.Protocol)
routeName := listenerName
listeners = append(listeners, ToListener(listenerName, routeName, util.If(isFargateMode, port.EnvoyListenerPort, port.ContainerPort), port.Protocol, isFargateMode))

View File

@@ -2,17 +2,16 @@ package controlplane
import (
"context"
"fmt"
"github.com/envoyproxy/go-control-plane/pkg/cache/v3"
serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func Main(ctx context.Context, filename string, port uint, logger *log.Logger) error {
func Main(ctx context.Context, factory cmdutil.Factory, port uint, logger *log.Entry) error {
snapshotCache := cache.NewSnapshotCache(false, cache.IDHash{}, logger)
proc := NewProcessor(snapshotCache, logger)
@@ -25,33 +24,20 @@ func Main(ctx context.Context, filename string, port uint, logger *log.Logger) e
notifyCh := make(chan NotifyMessage, 100)
notifyCh <- NotifyMessage{
Operation: Create,
FilePath: filename,
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return fmt.Errorf("failed to create file watcher: %v", err)
}
defer watcher.Close()
err = watcher.Add(filename)
if err != nil {
return fmt.Errorf("failed to add file: %s to wather: %v", filename, err)
}
notifyCh <- NotifyMessage{}
go func() {
errChan <- Watch(watcher, filename, notifyCh)
errChan <- Watch(ctx, factory, notifyCh)
}()
for {
select {
case msg := <-notifyCh:
err = proc.ProcessFile(msg)
err := proc.ProcessFile(msg)
if err != nil {
plog.G(ctx).Errorf("Failed to process file: %v", err)
return err
}
case err = <-errChan:
case err := <-errChan:
return err
case <-ctx.Done():
return ctx.Err()

View File

@@ -3,10 +3,8 @@ package controlplane
import (
"context"
"encoding/json"
"fmt"
"math"
"math/rand"
"os"
"reflect"
"strconv"
"time"
@@ -23,13 +21,13 @@ import (
type Processor struct {
cache cache.SnapshotCache
logger *log.Logger
logger *log.Entry
version int64
expireCache *utilcache.Expiring
}
func NewProcessor(cache cache.SnapshotCache, log *log.Logger) *Processor {
func NewProcessor(cache cache.SnapshotCache, log *log.Entry) *Processor {
return &Processor{
cache: cache,
logger: log,
@@ -47,9 +45,9 @@ func (p *Processor) newVersion() string {
}
func (p *Processor) ProcessFile(file NotifyMessage) error {
configList, err := ParseYaml(file.FilePath)
configList, err := ParseYaml(file.Content)
if err != nil {
p.logger.Errorf("error parsing yaml file: %+v", err)
p.logger.Errorf("failed to parse config file: %v", err)
return err
}
enableIPv6, _ := util.DetectSupportIPv6()
@@ -57,13 +55,21 @@ func (p *Processor) ProcessFile(file NotifyMessage) error {
if len(config.Uid) == 0 {
continue
}
lastConfig, ok := p.expireCache.Get(config.Uid)
var marshal []byte
marshal, err = json.Marshal(config)
if err != nil {
p.logger.Errorf("failed to marshal config: %v", err)
return err
}
uid := util.GenEnvoyUID(config.Namespace, config.Uid)
lastConfig, ok := p.expireCache.Get(uid)
if ok && reflect.DeepEqual(lastConfig.(*Virtual), config) {
marshal, _ := json.Marshal(config)
p.logger.Debugf("config are same, not needs to update, config: %s", string(marshal))
p.logger.Infof("no need to update, config: %s", string(marshal))
continue
}
p.logger.Debugf("update config, version %d, config %v", p.version, config)
p.logger.Infof("update config, version: %d, config: %s", p.version, marshal)
listeners, clusters, routes, endpoints := config.To(enableIPv6, p.logger)
resources := map[resource.Type][]types.Resource{
@@ -74,38 +80,33 @@ func (p *Processor) ProcessFile(file NotifyMessage) error {
resource.RuntimeType: {}, // runtimes
resource.SecretType: {}, // secrets
}
var snapshot *cache.Snapshot
snapshot, err = cache.NewSnapshot(p.newVersion(), resources)
if err != nil {
p.logger.Errorf("snapshot inconsistency: %v, err: %v", snapshot, err)
p.logger.Errorf("failed to snapshot inconsistency: %v", err)
return err
}
if err = snapshot.Consistent(); err != nil {
p.logger.Errorf("snapshot inconsistency: %v, err: %v", snapshot, err)
p.logger.Errorf("failed to snapshot inconsistency: %v", err)
return err
}
p.logger.Debugf("will serve snapshot %+v, nodeID: %s", snapshot, config.Uid)
if err = p.cache.SetSnapshot(context.Background(), config.Uid, snapshot); err != nil {
p.logger.Infof("will serve snapshot %+v, nodeID: %s", snapshot, uid)
err = p.cache.SetSnapshot(context.Background(), uid, snapshot)
if err != nil {
p.logger.Errorf("snapshot error %q for %v", err, snapshot)
return err
}
p.expireCache.Set(config.Uid, config, time.Minute*5)
p.expireCache.Set(uid, config, time.Minute*5)
}
return nil
}
func ParseYaml(file string) ([]*Virtual, error) {
func ParseYaml(content string) ([]*Virtual, error) {
var virtualList = make([]*Virtual, 0)
yamlFile, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("Error reading YAML file: %s\n", err)
}
err = yaml.Unmarshal(yamlFile, &virtualList)
err := yaml.Unmarshal([]byte(content), &virtualList)
if err != nil {
return nil, err
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"time"
clusterservice "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3"
discoverygrpc "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
@@ -14,6 +15,7 @@ import (
secretservice "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3"
serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
@@ -23,8 +25,17 @@ const (
)
func RunServer(ctx context.Context, server serverv3.Server, port uint) error {
grpcServer := grpc.NewServer(grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams))
grpcOpts := []grpc.ServerOption{
grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams),
grpc.KeepaliveParams(keepalive.ServerParameters{
Time: 15 * time.Second,
Timeout: 5 * time.Second,
}),
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
MinTime: 15 * time.Second,
PermitWithoutStream: true,
})}
grpcServer := grpc.NewServer(grpcOpts...)
var lc net.ListenConfig
listener, err := lc.Listen(ctx, "tcp", fmt.Sprintf(":%d", port))
if err != nil {

View File

@@ -1,62 +1,82 @@
package controlplane
import (
"fmt"
"context"
"time"
"github.com/fsnotify/fsnotify"
)
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
informerv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
type OperationType int
const (
Create OperationType = iota
Remove
Modify
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
type NotifyMessage struct {
Operation OperationType
FilePath string
Content string
}
func Watch(watcher *fsnotify.Watcher, filename string, notifyCh chan<- NotifyMessage) error {
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return fmt.Errorf("watcher has closed")
}
if event.Op&fsnotify.Write == fsnotify.Write {
notifyCh <- NotifyMessage{
Operation: Modify,
FilePath: event.Name,
}
} else if event.Op&fsnotify.Create == fsnotify.Create {
notifyCh <- NotifyMessage{
Operation: Create,
FilePath: event.Name,
}
} else if event.Op&fsnotify.Remove == fsnotify.Remove {
notifyCh <- NotifyMessage{
Operation: Remove,
FilePath: event.Name,
}
}
func Watch(ctx context.Context, f cmdutil.Factory, notifyCh chan<- NotifyMessage) error {
namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
restConfig, err := f.ToRESTConfig()
if err != nil {
return err
}
conf := rest.CopyConfig(restConfig)
conf.QPS = 1
conf.Burst = 2
clientSet, err := kubernetes.NewForConfig(conf)
if err != nil {
plog.G(ctx).Errorf("Failed to create clientset: %v", err)
return err
}
cmIndexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
cmInformer := informerv1.NewFilteredConfigMapInformer(clientSet, namespace, 0, cmIndexers, func(options *metav1.ListOptions) {
options.FieldSelector = fields.OneTermEqualSelector("metadata.name", config.ConfigMapPodTrafficManager).String()
})
cmTicker := time.NewTicker(time.Second * 5)
_, err = cmInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
cmTicker.Reset(time.Nanosecond * 1)
},
UpdateFunc: func(oldObj, newObj interface{}) {
cmTicker.Reset(time.Nanosecond * 1)
},
DeleteFunc: func(obj interface{}) {
cmTicker.Reset(time.Nanosecond * 1)
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return err
}
case err, ok := <-watcher.Errors:
if !ok {
return fmt.Errorf("watcher error closed")
}
return err
case <-ticker.C:
notifyCh <- NotifyMessage{
Operation: Modify,
FilePath: filename,
go cmInformer.Run(ctx.Done())
defer cmTicker.Stop()
for ; ctx.Err() == nil; <-cmTicker.C {
cmTicker.Reset(time.Second * 5)
cmList := cmInformer.GetIndexer().List()
if len(cmList) == 0 {
continue
}
for _, cm := range cmList {
configMap, ok := cm.(*v1.ConfigMap)
if ok {
if configMap.Data == nil {
configMap.Data = make(map[string]string)
}
notifyCh <- NotifyMessage{Content: configMap.Data[config.KeyEnvoy]}
continue
}
}
}
return ctx.Err()
}

74
pkg/core/bufferedtcp.go Normal file
View File

@@ -0,0 +1,74 @@
package core
import (
"context"
"errors"
"net"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
type bufferedTCP struct {
net.Conn
Chan chan *DatagramPacket
closed bool
}
func NewBufferedTCP(ctx context.Context, conn net.Conn) net.Conn {
c := &bufferedTCP{
Conn: conn,
Chan: make(chan *DatagramPacket, MaxSize),
}
go c.Run(ctx)
return c
}
func (c *bufferedTCP) Write(b []byte) (n int, err error) {
if c.closed {
return 0, errors.New("tcp channel is closed")
}
if len(b) == 0 {
return 0, nil
}
buf := config.LPool.Get().([]byte)[:]
n = copy(buf, b)
c.Chan <- newDatagramPacket(buf, n)
return n, nil
}
func (c *bufferedTCP) Run(ctx context.Context) {
for ctx.Err() == nil {
var buf *DatagramPacket
select {
case buf = <-c.Chan:
if buf == nil {
return
}
case <-ctx.Done():
return
}
_, err := c.Conn.Write(buf.Data[:buf.DataLength])
config.LPool.Put(buf.Data[:])
if err != nil {
plog.G(context.Background()).Errorf("[TCP] Write packet failed: %v", err)
_ = c.Conn.Close()
c.closed = true
return
}
}
}
func (c *bufferedTCP) Close() error {
c.closed = true
for {
var buf *DatagramPacket
select {
case buf = <-c.Chan:
config.LPool.Put(buf.Data[:])
default:
return c.Conn.Close()
}
}
}

View File

@@ -1,97 +0,0 @@
package core
import (
"context"
"errors"
"math"
"net"
)
var (
// ErrorEmptyChain is an error that implies the chain is empty.
ErrorEmptyChain = errors.New("empty chain")
)
type Chain struct {
retries int
node *Node
}
func NewChain(retry int, node *Node) *Chain {
return &Chain{retries: retry, node: node}
}
func (c *Chain) Node() *Node {
return c.node
}
func (c *Chain) IsEmpty() bool {
return c == nil || c.node == nil
}
func (c *Chain) DialContext(ctx context.Context) (conn net.Conn, err error) {
for i := 0; i < int(math.Max(float64(1), float64(c.retries))); i++ {
conn, err = c.dial(ctx)
if err == nil {
break
}
}
return
}
func (c *Chain) dial(ctx context.Context) (net.Conn, error) {
if c.IsEmpty() {
return nil, ErrorEmptyChain
}
conn, err := c.getConn(ctx)
if err != nil {
return nil, err
}
var cc net.Conn
cc, err = c.Node().Client.ConnectContext(ctx, conn)
if err != nil {
_ = conn.Close()
return nil, err
}
return cc, nil
}
func (*Chain) resolve(addr string) string {
if host, port, err := net.SplitHostPort(addr); err == nil {
if ips, err := net.LookupIP(host); err == nil && len(ips) > 0 {
return net.JoinHostPort(ips[0].String(), port)
}
}
return addr
}
func (c *Chain) getConn(ctx context.Context) (net.Conn, error) {
if c.IsEmpty() {
return nil, ErrorEmptyChain
}
return c.Node().Client.Dial(ctx, c.resolve(c.Node().Addr))
}
type Handler interface {
Handle(ctx context.Context, conn net.Conn)
}
type Client struct {
Connector
Transporter
}
type Connector interface {
ConnectContext(ctx context.Context, conn net.Conn) (net.Conn, error)
}
type Transporter interface {
Dial(ctx context.Context, addr string) (net.Conn, error)
}
type Server struct {
Listener net.Listener
Handler Handler
}

96
pkg/core/forwarder.go Normal file
View File

@@ -0,0 +1,96 @@
package core
import (
"context"
"errors"
"net"
)
var (
// ErrorEmptyForwarder is an error that implies the forward is empty.
ErrorEmptyForwarder = errors.New("empty forwarder")
)
type Forwarder struct {
retries int
node *Node
}
func NewForwarder(retry int, node *Node) *Forwarder {
return &Forwarder{retries: retry, node: node}
}
func (c *Forwarder) Node() *Node {
return c.node
}
func (c *Forwarder) IsEmpty() bool {
return c == nil || c.node == nil
}
func (c *Forwarder) DialContext(ctx context.Context) (conn net.Conn, err error) {
for i := 0; i < max(1, c.retries); i++ {
conn, err = c.dial(ctx)
if err == nil {
break
}
}
return
}
func (c *Forwarder) dial(ctx context.Context) (net.Conn, error) {
if c.IsEmpty() {
return nil, ErrorEmptyForwarder
}
conn, err := c.getConn(ctx)
if err != nil {
return nil, err
}
var cc net.Conn
cc, err = c.Node().Client.ConnectContext(ctx, conn)
if err != nil {
_ = conn.Close()
return nil, err
}
return cc, nil
}
func (*Forwarder) resolve(addr string) string {
if host, port, err := net.SplitHostPort(addr); err == nil {
if ips, err := net.LookupIP(host); err == nil && len(ips) > 0 {
return net.JoinHostPort(ips[0].String(), port)
}
}
return addr
}
func (c *Forwarder) getConn(ctx context.Context) (net.Conn, error) {
if c.IsEmpty() {
return nil, ErrorEmptyForwarder
}
return c.Node().Client.Dial(ctx, c.Node().Addr)
}
type Handler interface {
Handle(ctx context.Context, conn net.Conn)
}
type Client struct {
Connector
Transporter
}
type Connector interface {
ConnectContext(ctx context.Context, conn net.Conn) (net.Conn, error)
}
type Transporter interface {
Dial(ctx context.Context, addr string) (net.Conn, error)
}
type Server struct {
Listener net.Listener
Handler Handler
}

View File

@@ -6,22 +6,14 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func ICMPForwarder(s *stack.Stack, ctx context.Context) func(stack.TransportEndpointID, *stack.PacketBuffer) bool {
return func(id stack.TransportEndpointID, buffer *stack.PacketBuffer) bool {
plog.G(ctx).Debugf("[TUN-ICMP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
func ICMPForwarder(ctx context.Context, s *stack.Stack) func(stack.TransportEndpointID, *stack.PacketBuffer) bool {
return func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
defer pkt.DecRef()
plog.G(ctx).Infof("[TUN-ICMP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
ctx1, cancelFunc := context.WithCancel(ctx)
defer cancelFunc()
ok, err := util.PingOnce(ctx1, id.RemoteAddress.String(), id.LocalAddress.String())
if err != nil {
plog.G(ctx).Debugf("[TUN-ICMP] Failed to ping dst %s from src %s",
id.LocalAddress.String(), id.RemoteAddress.String(),
)
}
return ok
return true
}
}

104
pkg/core/gvisorlocalstack.go Executable file
View File

@@ -0,0 +1,104 @@
package core
import (
"context"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/packetsocket"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/raw"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func NewLocalStack(ctx context.Context, tun stack.LinkEndpoint) *stack.Stack {
nicID := tcpip.NICID(1)
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
ipv6.NewProtocol,
},
TransportProtocols: []stack.TransportProtocolFactory{
tcp.NewProtocol,
udp.NewProtocol,
},
Clock: tcpip.NewStdClock(),
AllowPacketEndpointWrite: true,
HandleLocal: false, // if set to true, ping local ip will fail
// Enable raw sockets for users with sufficient
// privileges.
RawFactory: raw.EndpointFactory{},
})
// set handler for TCP UDP ICMP
s.SetTransportProtocolHandler(tcp.ProtocolNumber, LocalTCPForwarder(ctx, s))
s.SetTransportProtocolHandler(udp.ProtocolNumber, LocalUDPForwarder(ctx, s))
s.SetTransportProtocolHandler(header.ICMPv4ProtocolNumber, ICMPForwarder(ctx, s))
s.SetTransportProtocolHandler(header.ICMPv6ProtocolNumber, ICMPForwarder(ctx, s))
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: nicID,
},
{
Destination: header.IPv6EmptySubnet,
NIC: nicID,
},
})
s.CreateNICWithOptions(nicID, packetsocket.New(tun), stack.NICOptions{
Disabled: false,
Context: ctx,
})
s.SetPromiscuousMode(nicID, true)
s.SetSpoofing(nicID, true)
// Enable SACK Recovery.
{
opt := tcpip.TCPSACKEnabled(true)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
plog.G(ctx).Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %v", tcp.ProtocolNumber, opt, opt, err)
}
}
// Set default TTLs as required by socket/netstack.
{
opt := tcpip.DefaultTTLOption(64)
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
plog.G(ctx).Fatalf("SetNetworkProtocolOption(%d, &%T(%d)): %v", ipv4.ProtocolNumber, opt, opt, err)
}
if err := s.SetNetworkProtocolOption(ipv6.ProtocolNumber, &opt); err != nil {
plog.G(ctx).Fatalf("SetNetworkProtocolOption(%d, &%T(%d)): %v", ipv6.ProtocolNumber, opt, opt, err)
}
}
// Enable Receive Buffer Auto-Tuning.
{
opt := tcpip.TCPModerateReceiveBufferOption(true)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
plog.G(ctx).Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %v", tcp.ProtocolNumber, opt, opt, err)
}
}
{
if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, true); err != nil {
plog.G(ctx).Fatalf("Set IPv4 forwarding: %v", err)
}
if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, true); err != nil {
plog.G(ctx).Fatalf("Set IPv6 forwarding: %v", err)
}
}
{
option := tcpip.TCPModerateReceiveBufferOption(true)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &option); err != nil {
plog.G(ctx).Fatalf("Set TCP moderate receive buffer: %v", err)
}
}
return s
}

View File

@@ -0,0 +1,84 @@
package core
import (
"context"
"fmt"
"io"
"net"
"time"
"github.com/pkg/errors"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func LocalTCPForwarder(ctx context.Context, s *stack.Stack) func(stack.TransportEndpointID, *stack.PacketBuffer) bool {
return tcp.NewForwarder(s, 0, 100000, func(request *tcp.ForwarderRequest) {
ctx = context.Background()
id := request.ID()
plog.G(ctx).Infof("[TUN-TCP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
w := &waiter.Queue{}
endpoint, tErr := request.CreateEndpoint(w)
if tErr != nil {
plog.G(ctx).Errorf("[TUN-TCP] Failed to create endpoint: %v", tErr)
request.Complete(true)
return
}
defer endpoint.Close()
conn := gonet.NewTCPConn(w, endpoint)
defer conn.Close()
var err error
defer func() {
if err != nil && !errors.Is(err, io.EOF) {
request.Complete(true)
} else {
request.Complete(false)
}
}()
// 2, dial proxy
var host string
if id.LocalAddress.To4() != (tcpip.Address{}) {
host = "127.0.0.1"
} else {
host = net.IPv6loopback.String()
}
port := fmt.Sprintf("%d", id.LocalPort)
var d = net.Dialer{Timeout: time.Second * 5}
var remote net.Conn
remote, err = d.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
if err != nil {
plog.G(ctx).Errorf("[TUN-TCP] Failed to connect addr %s: %v", net.JoinHostPort(host, port), err)
return
}
defer remote.Close()
errChan := make(chan error, 2)
go func() {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
written, err2 := io.CopyBuffer(remote, conn, buf)
plog.G(ctx).Infof("[TUN-TCP] Write length %d data to remote", written)
errChan <- err2
}()
go func() {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
written, err2 := io.CopyBuffer(conn, remote, buf)
plog.G(ctx).Infof("[TUN-TCP] Read length %d data from remote", written)
errChan <- err2
}()
err = <-errChan
if err != nil && !errors.Is(err, io.EOF) {
plog.G(ctx).Errorf("[TUN-TCP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err)
}
}).HandlePacket
}

View File

@@ -0,0 +1,60 @@
package core
import (
"context"
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
type gvisorLocalHandler struct {
// read from tcp conn write to gvisor inbound
gvisorInbound <-chan *Packet
// write to tcp conn
gvisorOutbound chan<- *Packet
outbound chan<- *Packet
errChan chan error
}
func handleGvisorPacket(gvisorInbound <-chan *Packet, outbound chan<- *Packet) *gvisorLocalHandler {
return &gvisorLocalHandler{
gvisorInbound: gvisorInbound,
outbound: outbound,
errChan: make(chan error, 1),
}
}
func (h *gvisorLocalHandler) Run(ctx context.Context) {
endpoint := channel.New(tcp.DefaultReceiveBufferSize, uint32(config.DefaultMTU), tcpip.GetRandMacAddr())
// for support ipv6 skip checksum
// vendor/gvisor.dev/gvisor/pkg/tcpip/stack/nic.go:763
endpoint.LinkEPCapabilities = stack.CapabilityRXChecksumOffload
defer endpoint.Close()
go func() {
defer util.HandleCrash()
readFromGvisorInboundWriteToEndpoint(ctx, h.gvisorInbound, endpoint)
util.SafeClose(h.errChan)
}()
go func() {
defer util.HandleCrash()
readFromEndpointWriteToTun(ctx, endpoint, h.outbound)
util.SafeClose(h.errChan)
}()
s := NewLocalStack(ctx, sniffer.NewWithPrefix(endpoint, fmt.Sprintf("[gVISOR]%s ", plog.GenStr(plog.GetFields(ctx)))))
defer s.Destroy()
select {
case <-h.errChan:
return
case <-ctx.Done():
return
}
}

View File

@@ -0,0 +1,70 @@
package core
import (
"context"
"fmt"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func readFromEndpointWriteToTun(ctx context.Context, endpoint *channel.Endpoint, out chan<- *Packet) {
prefix := fmt.Sprintf("[gVISOR]%s ", plog.GenStr(plog.GetFields(ctx)))
for ctx.Err() == nil {
pkt := endpoint.ReadContext(ctx)
if pkt != nil {
sniffer.LogPacket(prefix, sniffer.DirectionSend, pkt.NetworkProtocolNumber, pkt)
data := pkt.ToView().AsSlice()
buf := config.LPool.Get().([]byte)[:]
n := copy(buf[1:], data)
buf[0] = 0
out <- NewPacket(buf[:], n+1, nil, nil)
}
}
}
func readFromGvisorInboundWriteToEndpoint(ctx context.Context, in <-chan *Packet, endpoint *channel.Endpoint) {
prefix := fmt.Sprintf("[gVISOR]%s ", plog.GenStr(plog.GetFields(ctx)))
for ctx.Err() == nil {
var packet *Packet
select {
case packet = <-in:
if packet == nil {
return
}
case <-ctx.Done():
return
}
// Try to determine network protocol number, default zero.
var protocol tcpip.NetworkProtocolNumber
// TUN interface with IFF_NO_PI enabled, thus
// we need to determine protocol from version field
if util.IsIPv4(packet.data[1:packet.length]) {
protocol = header.IPv4ProtocolNumber
} else if util.IsIPv6(packet.data[1:packet.length]) {
protocol = header.IPv6ProtocolNumber
} else {
plog.G(ctx).Errorf("[TCP-GVISOR] Unknown packet")
config.LPool.Put(packet.data[:])
continue
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: 0,
Payload: buffer.MakeWithData(packet.data[1:packet.length]),
})
config.LPool.Put(packet.data[:])
sniffer.LogPacket(prefix, sniffer.DirectionRecv, protocol, pkt)
endpoint.InjectInbound(protocol, pkt)
pkt.DecRef()
}
}

View File

@@ -0,0 +1,127 @@
package core
import (
"context"
"io"
"net"
"time"
"github.com/pkg/errors"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"gvisor.dev/gvisor/pkg/waiter"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func LocalUDPForwarder(ctx context.Context, s *stack.Stack) func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
return udp.NewForwarder(s, func(request *udp.ForwarderRequest) {
id := request.ID()
plog.G(ctx).Infof("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
src := &net.UDPAddr{
IP: id.RemoteAddress.AsSlice(),
Port: int(id.RemotePort),
}
var ip net.IP
if id.LocalAddress.To4() != (tcpip.Address{}) {
ip = net.ParseIP("127.0.0.1")
} else {
ip = net.IPv6loopback
}
dst := &net.UDPAddr{
IP: ip,
Port: int(id.LocalPort),
}
w := &waiter.Queue{}
endpoint, tErr := request.CreateEndpoint(w)
if tErr != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to create endpoint to dst: %s: %v", dst.String(), tErr)
return
}
// dial dst
remote, err1 := net.DialUDP("udp", nil, dst)
if err1 != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to connect dst: %s: %v", dst.String(), err1)
return
}
conn := gonet.NewUDPConn(w, endpoint)
go func() {
defer conn.Close()
defer remote.Close()
errChan := make(chan error, 2)
go func() {
defer util.HandleCrash()
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
var written int
var err error
for {
err = conn.SetReadDeadline(time.Now().Add(time.Second * 120))
if err != nil {
break
}
var read int
read, _, err = conn.ReadFrom(buf[:])
if err != nil {
break
}
written += read
err = remote.SetWriteDeadline(time.Now().Add(time.Second * 120))
if err != nil {
break
}
_, err = remote.Write(buf[:read])
if err != nil {
break
}
}
plog.G(ctx).Infof("[TUN-UDP] Write length %d data from src: %s -> dst: %s", written, src, dst)
errChan <- err
}()
go func() {
defer util.HandleCrash()
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
var err error
var written int
for {
err = remote.SetReadDeadline(time.Now().Add(time.Second * 120))
if err != nil {
break
}
var n int
n, _, err = remote.ReadFromUDP(buf[:])
if err != nil {
break
}
written += n
err = conn.SetWriteDeadline(time.Now().Add(time.Second * 120))
if err != nil {
break
}
_, err = conn.Write(buf[:n])
if err != nil {
break
}
}
plog.G(ctx).Infof("[TUN-UDP] Read length %d data from dst: %s -> src: %s", written, dst, src)
errChan <- err
}()
err1 = <-errChan
if err1 != nil && !errors.Is(err1, io.EOF) {
plog.G(ctx).Errorf("[TUN-UDP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err1)
}
}()
}).HandlePacket
}

View File

@@ -35,10 +35,10 @@ func NewStack(ctx context.Context, tun stack.LinkEndpoint) *stack.Stack {
RawFactory: raw.EndpointFactory{},
})
// set handler for TCP UDP ICMP
s.SetTransportProtocolHandler(tcp.ProtocolNumber, TCPForwarder(s, ctx))
s.SetTransportProtocolHandler(udp.ProtocolNumber, UDPForwarder(s, ctx))
s.SetTransportProtocolHandler(header.ICMPv4ProtocolNumber, ICMPForwarder(s, ctx))
s.SetTransportProtocolHandler(header.ICMPv6ProtocolNumber, ICMPForwarder(s, ctx))
s.SetTransportProtocolHandler(tcp.ProtocolNumber, TCPForwarder(ctx, s))
s.SetTransportProtocolHandler(udp.ProtocolNumber, UDPForwarder(ctx, s))
s.SetTransportProtocolHandler(header.ICMPv4ProtocolNumber, ICMPForwarder(ctx, s))
s.SetTransportProtocolHandler(header.ICMPv6ProtocolNumber, ICMPForwarder(ctx, s))
s.SetRouteTable([]tcpip.Route{
{

View File

@@ -1,16 +1,13 @@
package core
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"io"
"net"
"time"
"github.com/pkg/errors"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
@@ -20,108 +17,59 @@ import (
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func TCPForwarder(s *stack.Stack, ctx context.Context) func(stack.TransportEndpointID, *stack.PacketBuffer) bool {
func TCPForwarder(ctx context.Context, s *stack.Stack) func(stack.TransportEndpointID, *stack.PacketBuffer) bool {
return tcp.NewForwarder(s, 0, 100000, func(request *tcp.ForwarderRequest) {
defer request.Complete(false)
id := request.ID()
plog.G(ctx).Debugf("[TUN-TCP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
plog.G(ctx).Infof("[TUN-TCP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
w := &waiter.Queue{}
endpoint, tErr := request.CreateEndpoint(w)
if tErr != nil {
plog.G(ctx).Errorf("[TUN-TCP] Failed to create endpoint: %v", tErr)
request.Complete(true)
return
}
conn := gonet.NewTCPConn(w, endpoint)
defer conn.Close()
var err error
defer func() {
if err != nil && !errors.Is(err, io.EOF) {
request.Complete(true)
} else {
request.Complete(false)
}
}()
// 2, dial proxy
host := id.LocalAddress.String()
port := fmt.Sprintf("%d", id.LocalPort)
var remote net.Conn
var d = net.Dialer{Timeout: time.Second * 5}
remote, err := d.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
remote, err = d.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
if err != nil {
plog.G(ctx).Errorf("[TUN-TCP] Failed to connect addr %s: %v", net.JoinHostPort(host, port), err)
return
}
w := &waiter.Queue{}
endpoint, tErr := request.CreateEndpoint(w)
if tErr != nil {
plog.G(ctx).Debugf("[TUN-TCP] Failed to create endpoint: %v", tErr)
return
}
conn := gonet.NewTCPConn(w, endpoint)
defer conn.Close()
defer remote.Close()
errChan := make(chan error, 2)
go func() {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
written, err2 := io.CopyBuffer(remote, conn, buf)
plog.G(ctx).Debugf("[TUN-TCP] Write length %d data to remote", written)
plog.G(ctx).Infof("[TUN-TCP] Write length %d data to remote", written)
errChan <- err2
}()
go func() {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
written, err2 := io.CopyBuffer(conn, remote, buf)
plog.G(ctx).Debugf("[TUN-TCP] Read length %d data from remote", written)
plog.G(ctx).Infof("[TUN-TCP] Read length %d data from remote", written)
errChan <- err2
}()
err = <-errChan
if err != nil && !errors.Is(err, io.EOF) {
plog.G(ctx).Debugf("[TUN-TCP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err)
plog.G(ctx).Errorf("[TUN-TCP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err)
}
}).HandlePacket
}
func WriteProxyInfo(conn net.Conn, id stack.TransportEndpointID) error {
var b bytes.Buffer
i := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(i[:])
binary.BigEndian.PutUint16(i, id.LocalPort)
b.Write(i)
binary.BigEndian.PutUint16(i, id.RemotePort)
b.Write(i)
b.WriteByte(byte(id.LocalAddress.Len()))
b.Write(id.LocalAddress.AsSlice())
b.WriteByte(byte(id.RemoteAddress.Len()))
b.Write(id.RemoteAddress.AsSlice())
_, err := b.WriteTo(conn)
return err
}
// ParseProxyInfo parse proxy info [20]byte
func ParseProxyInfo(conn net.Conn) (id stack.TransportEndpointID, err error) {
var n int
var port = make([]byte, 2)
// local port
if n, err = io.ReadFull(conn, port); err != nil || n != 2 {
return
}
id.LocalPort = binary.BigEndian.Uint16(port)
// remote port
if n, err = io.ReadFull(conn, port); err != nil || n != 2 {
return
}
id.RemotePort = binary.BigEndian.Uint16(port)
// local address
if n, err = io.ReadFull(conn, port[:1]); err != nil || n != 1 {
return
}
var localAddress = make([]byte, port[0])
if n, err = io.ReadFull(conn, localAddress); err != nil || n != len(localAddress) {
return
}
id.LocalAddress = tcpip.AddrFromSlice(localAddress)
// remote address
if n, err = io.ReadFull(conn, port[:1]); err != nil || n != 1 {
return
}
var remoteAddress = make([]byte, port[0])
if n, err = io.ReadFull(conn, remoteAddress); err != nil || n != len(remoteAddress) {
return
}
id.RemoteAddress = tcpip.AddrFromSlice(remoteAddress)
return
}

View File

@@ -2,12 +2,15 @@ package core
import (
"context"
"crypto/tls"
"errors"
"net"
"sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/sniffer"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
@@ -18,13 +21,11 @@ import (
type gvisorTCPHandler struct {
// map[srcIP]net.Conn
routeMapTCP *sync.Map
packetChan chan *datagramPacket
}
func GvisorTCPHandler() Handler {
return &gvisorTCPHandler{
routeMapTCP: RouteMapTCP,
packetChan: TCPPacketChan,
}
}
@@ -32,16 +33,19 @@ func (h *gvisorTCPHandler) Handle(ctx context.Context, tcpConn net.Conn) {
defer tcpConn.Close()
cancel, cancelFunc := context.WithCancel(ctx)
defer cancelFunc()
plog.G(ctx).Debugf("[TCP] %s -> %s", tcpConn.RemoteAddr(), tcpConn.LocalAddr())
plog.G(ctx).Infof("[TUN-GVISOR] %s -> %s", tcpConn.RemoteAddr(), tcpConn.LocalAddr())
h.handle(cancel, tcpConn)
}
func (h *gvisorTCPHandler) handle(ctx context.Context, tcpConn net.Conn) {
endpoint := channel.New(tcp.DefaultReceiveBufferSize, uint32(config.DefaultMTU), tcpip.GetRandMacAddr())
// for support ipv6 skip checksum
// vendor/gvisor.dev/gvisor/pkg/tcpip/stack/nic.go:763
endpoint.LinkEPCapabilities = stack.CapabilityRXChecksumOffload
errChan := make(chan error, 2)
go func() {
defer util.HandleCrash()
h.readFromTCPConnWriteToEndpoint(ctx, tcpConn, endpoint)
h.readFromTCPConnWriteToEndpoint(ctx, NewBufferedTCP(ctx, tcpConn), endpoint)
util.SafeClose(errChan)
}()
go func() {
@@ -49,8 +53,8 @@ func (h *gvisorTCPHandler) handle(ctx context.Context, tcpConn net.Conn) {
h.readFromEndpointWriteToTCPConn(ctx, tcpConn, endpoint)
util.SafeClose(errChan)
}()
stack := NewStack(ctx, sniffer.NewWithPrefix(endpoint, "[gVISOR] "))
defer stack.Destroy()
s := NewStack(ctx, sniffer.NewWithPrefix(endpoint, "[gVISOR] "))
defer s.Destroy()
select {
case <-errChan:
return
@@ -60,14 +64,25 @@ func (h *gvisorTCPHandler) handle(ctx context.Context, tcpConn net.Conn) {
}
func GvisorTCPListener(addr string) (net.Listener, error) {
plog.G(context.Background()).Debugf("Gvisor TCP listening addr: %s", addr)
plog.G(context.Background()).Infof("Gvisor TCP listening addr: %s", addr)
laddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
ln, err := net.ListenTCP("tcp", laddr)
listener, err := net.ListenTCP("tcp", laddr)
if err != nil {
return nil, err
}
return &tcpKeepAliveListener{TCPListener: ln}, nil
serverConfig, err := util.GetTlsServerConfig(nil)
if err != nil {
if errors.Is(err, util.ErrNoTLSConfig) {
plog.G(context.Background()).Warn("tls config not found in config, use raw tcp mode")
return &tcpKeepAliveListener{TCPListener: listener}, nil
}
plog.G(context.Background()).Errorf("failed to get tls server config: %v", err)
_ = listener.Close()
return nil, err
}
plog.G(context.Background()).Debugf("Use tls mode")
return tls.NewListener(&tcpKeepAliveListener{TCPListener: listener}, serverConfig), nil
}

View File

@@ -20,21 +20,19 @@ import (
)
func (h *gvisorTCPHandler) readFromEndpointWriteToTCPConn(ctx context.Context, conn net.Conn, endpoint *channel.Endpoint) {
tcpConn, _ := newGvisorFakeUDPTunnelConnOverTCP(ctx, conn)
for {
select {
case <-ctx.Done():
return
default:
}
pktBuffer := endpoint.ReadContext(ctx)
if pktBuffer != nil {
sniffer.LogPacket("[gVISOR] ", sniffer.DirectionSend, pktBuffer.NetworkProtocolNumber, pktBuffer)
buf := pktBuffer.ToView().AsSlice()
_, err := tcpConn.Write(buf)
tcpConn, _ := newGvisorUDPConnOverTCP(ctx, conn)
for ctx.Err() == nil {
pkt := endpoint.ReadContext(ctx)
if pkt != nil {
sniffer.LogPacket("[gVISOR] ", sniffer.DirectionSend, pkt.NetworkProtocolNumber, pkt)
data := pkt.ToView().AsSlice()
buf := config.LPool.Get().([]byte)[:]
n := copy(buf[1:], data)
buf[0] = 0
_, err := tcpConn.Write(buf[:n+1])
config.LPool.Put(buf[:])
if err != nil {
plog.G(ctx).Errorf("[TUN] Failed to write data to tun device: %v", err)
plog.G(ctx).Errorf("[TUN-GVISOR] Failed to write data to tun device: %v", err)
}
}
}
@@ -42,23 +40,20 @@ func (h *gvisorTCPHandler) readFromEndpointWriteToTCPConn(ctx context.Context, c
// tun --> dispatcher
func (h *gvisorTCPHandler) readFromTCPConnWriteToEndpoint(ctx context.Context, conn net.Conn, endpoint *channel.Endpoint) {
tcpConn, _ := newGvisorFakeUDPTunnelConnOverTCP(ctx, conn)
for {
select {
case <-ctx.Done():
return
default:
}
tcpConn, _ := newGvisorUDPConnOverTCP(ctx, conn)
defer tcpConn.Close()
defer h.removeFromRouteMapTCP(ctx, conn)
for ctx.Err() == nil {
buf := config.LPool.Get().([]byte)[:]
read, err := tcpConn.Read(buf[:])
if err != nil {
plog.G(ctx).Errorf("[TUN] Failed to read from tcp conn: %v", err)
plog.G(ctx).Errorf("[TCP-GVISOR] Failed to read from tcp conn: %v", err)
config.LPool.Put(buf[:])
return
}
if read == 0 {
plog.G(ctx).Warnf("[TUN] Read from tcp conn length is %d", read)
plog.G(ctx).Warnf("[TCP-GVISOR] Read from tcp conn length is %d", read)
config.LPool.Put(buf[:])
continue
}
@@ -68,9 +63,9 @@ func (h *gvisorTCPHandler) readFromTCPConnWriteToEndpoint(ctx context.Context, c
var src, dst net.IP
// TUN interface with IFF_NO_PI enabled, thus
// we need to determine protocol from version field
if util.IsIPv4(buf) {
if util.IsIPv4(buf[1:read]) {
protocol = header.IPv4ProtocolNumber
ipHeader, err := ipv4.ParseHeader(buf[:read])
ipHeader, err := ipv4.ParseHeader(buf[1:read])
if err != nil {
plog.G(ctx).Errorf("Failed to parse IPv4 header: %v", err)
config.LPool.Put(buf[:])
@@ -79,11 +74,11 @@ func (h *gvisorTCPHandler) readFromTCPConnWriteToEndpoint(ctx context.Context, c
ipProtocol = ipHeader.Protocol
src = ipHeader.Src
dst = ipHeader.Dst
} else if util.IsIPv6(buf) {
} else if util.IsIPv6(buf[1:read]) {
protocol = header.IPv6ProtocolNumber
ipHeader, err := ipv6.ParseHeader(buf[:read])
ipHeader, err := ipv6.ParseHeader(buf[1:read])
if err != nil {
plog.G(ctx).Errorf("Failed to parse IPv6 header: %s", err.Error())
plog.G(ctx).Errorf("[TCP-GVISOR] Failed to parse IPv6 header: %s", err.Error())
config.LPool.Put(buf[:])
continue
}
@@ -91,42 +86,63 @@ func (h *gvisorTCPHandler) readFromTCPConnWriteToEndpoint(ctx context.Context, c
src = ipHeader.Src
dst = ipHeader.Dst
} else {
plog.G(ctx).Debugf("[TUN-GVISOR] Unknown packet")
plog.G(ctx).Errorf("[TCP-GVISOR] Unknown packet")
config.LPool.Put(buf[:])
continue
}
h.addRoute(ctx, src, conn)
h.addToRouteMapTCP(ctx, src, conn)
// inner ip like 198.19.0.100/102/103 connect each other
if config.CIDR.Contains(dst) || config.CIDR6.Contains(dst) {
plog.G(ctx).Debugf("[TUN-RAW] Forward to TUN device, SRC: %s, DST: %s, Length: %d", src.String(), dst.String(), read)
util.SafeWrite(h.packetChan, &datagramPacket{
DataLength: uint16(read),
Data: buf[:],
// for issue 594, sometimes k8s service network CIDR also use CIDR 198.19.151.170
// if we can find dst in route map, just trade packet as inner communicate
// if not find dst in route map, just trade packet as k8s service/pod ip
if c, found := h.routeMapTCP.Load(dst.String()); found {
plog.G(ctx).Debugf("[TCP-GVISOR] Find TCP route SRC: %s to DST: %s -> %s", src, dst, c.(net.Conn).RemoteAddr())
dgram := newDatagramPacket(buf, read)
err = dgram.Write(c.(net.Conn))
config.LPool.Put(buf[:])
if err != nil {
plog.G(ctx).Errorf("[TCP-GVISOR] Failed to write to %s <- %s : %s", c.(net.Conn).RemoteAddr(), c.(net.Conn).LocalAddr(), err)
}
} else if buf[0] == 1 {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: 0,
Payload: buffer.MakeWithData(buf[1:read]),
})
continue
config.LPool.Put(buf[:])
sniffer.LogPacket("[gVISOR] ", sniffer.DirectionRecv, protocol, pkt)
endpoint.InjectInbound(protocol, pkt)
pkt.DecRef()
plog.G(ctx).Debugf("[TCP-GVISOR] Write to gvisor. SRC: %s, DST: %s, Protocol: %s, Length: %d", src, dst, layers.IPProtocol(ipProtocol).String(), read)
} else {
TCPPacketChan <- &Packet{
data: buf[:],
length: read,
src: src,
dst: dst,
}
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: 0,
Payload: buffer.MakeWithData(buf[:read]),
})
config.LPool.Put(buf[:])
sniffer.LogPacket("[gVISOR] ", sniffer.DirectionRecv, protocol, pkt)
endpoint.InjectInbound(protocol, pkt)
pkt.DecRef()
plog.G(ctx).Debugf("[TUN-%s] Write to Gvisor IP-Protocol: %s, SRC: %s, DST: %s, Length: %d", layers.IPProtocol(ipProtocol).String(), layers.IPProtocol(ipProtocol).String(), src.String(), dst, read)
}
}
func (h *gvisorTCPHandler) addRoute(ctx context.Context, src net.IP, tcpConn net.Conn) {
func (h *gvisorTCPHandler) addToRouteMapTCP(ctx context.Context, src net.IP, tcpConn net.Conn) {
value, loaded := h.routeMapTCP.LoadOrStore(src.String(), tcpConn)
if loaded {
if tcpConn != value.(net.Conn) {
if value.(net.Conn) != tcpConn {
h.routeMapTCP.Store(src.String(), tcpConn)
plog.G(ctx).Debugf("[TCP] Replace route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
plog.G(ctx).Infof("[TUN-GVISOR] Replace route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
}
} else {
plog.G(ctx).Debugf("[TCP] Add new route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
plog.G(ctx).Infof("[TUN-GVISOR] Add new route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
}
}
func (h *gvisorTCPHandler) removeFromRouteMapTCP(ctx context.Context, tcpConn net.Conn) {
h.routeMapTCP.Range(func(key, value any) bool {
if value.(net.Conn) == tcpConn {
h.routeMapTCP.Delete(key)
plog.G(ctx).Infof("[TCP-GVISOR] Delete to DST %s by conn %s from globle route map TCP", key, tcpConn.LocalAddr())
}
return true
})
}

View File

@@ -17,10 +17,10 @@ import (
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
func UDPForwarder(s *stack.Stack, ctx context.Context) func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
func UDPForwarder(ctx context.Context, s *stack.Stack) func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
return udp.NewForwarder(s, func(request *udp.ForwarderRequest) {
id := request.ID()
plog.G(ctx).Debugf("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
plog.G(ctx).Infof("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
src := &net.UDPAddr{
@@ -35,7 +35,7 @@ func UDPForwarder(s *stack.Stack, ctx context.Context) func(id stack.TransportEn
w := &waiter.Queue{}
endpoint, tErr := request.CreateEndpoint(w)
if tErr != nil {
plog.G(ctx).Debugf("[TUN-UDP] Failed to create endpoint to dst: %s: %v", dst.String(), tErr)
plog.G(ctx).Errorf("[TUN-UDP] Failed to create endpoint to dst: %s: %v", dst.String(), tErr)
return
}
@@ -78,7 +78,7 @@ func UDPForwarder(s *stack.Stack, ctx context.Context) func(id stack.TransportEn
break
}
}
plog.G(ctx).Debugf("[TUN-UDP] Write length %d data from src: %s -> dst: %s", written, src.String(), dst.String())
plog.G(ctx).Infof("[TUN-UDP] Write length %d data from src: %s -> dst: %s", written, src, dst)
errChan <- err
}()
go func() {
@@ -108,12 +108,12 @@ func UDPForwarder(s *stack.Stack, ctx context.Context) func(id stack.TransportEn
break
}
}
plog.G(ctx).Debugf("[TUN-UDP] Read length %d data from dst: %s -> src: %s", written, dst.String(), src.String())
plog.G(ctx).Infof("[TUN-UDP] Read length %d data from dst: %s -> src: %s", written, dst, src)
errChan <- err
}()
err1 = <-errChan
if err1 != nil && !errors.Is(err1, io.EOF) {
plog.G(ctx).Debugf("[TUN-UDP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err1)
plog.G(ctx).Errorf("[TUN-UDP] Disconnect: %s >-<: %s: %v", conn.LocalAddr(), remote.RemoteAddr(), err1)
}
}()
}).HandlePacket

View File

@@ -3,9 +3,13 @@ package core
import (
"context"
"fmt"
"io"
"net"
"time"
"github.com/pkg/errors"
"gvisor.dev/gvisor/pkg/tcpip"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
@@ -21,21 +25,27 @@ func (h *gvisorUDPHandler) Handle(ctx context.Context, tcpConn net.Conn) {
defer tcpConn.Close()
plog.G(ctx).Debugf("[TUN-UDP] %s -> %s", tcpConn.RemoteAddr(), tcpConn.LocalAddr())
// 1, get proxy info
endpointID, err := ParseProxyInfo(tcpConn)
id, err := util.ParseProxyInfo(tcpConn)
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to parse proxy info: %v", err)
return
}
plog.G(ctx).Debugf("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress %s",
endpointID.LocalPort, endpointID.LocalAddress.String(), endpointID.RemotePort, endpointID.RemoteAddress.String(),
plog.G(ctx).Infof("[TUN-UDP] LocalPort: %d, LocalAddress: %s, RemotePort: %d, RemoteAddress: %s",
id.LocalPort, id.LocalAddress.String(), id.RemotePort, id.RemoteAddress.String(),
)
// 2, dial proxy
addr := &net.UDPAddr{
IP: endpointID.LocalAddress.AsSlice(),
Port: int(endpointID.LocalPort),
IP: id.LocalAddress.AsSlice(),
Port: int(id.LocalPort),
}
var network string
if id.LocalAddress.To4() != (tcpip.Address{}) {
network = "udp4"
} else {
network = "udp6"
}
var remote *net.UDPConn
remote, err = net.DialUDP("udp", nil, addr)
remote, err = net.DialUDP(network, nil, addr)
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to connect addr %s: %v", addr.String(), err)
return
@@ -44,38 +54,42 @@ func (h *gvisorUDPHandler) Handle(ctx context.Context, tcpConn net.Conn) {
}
// fake udp connect over tcp
type gvisorFakeUDPTunnelConn struct {
type gvisorUDPConnOverTCP struct {
// tcp connection
net.Conn
ctx context.Context
}
func newGvisorFakeUDPTunnelConnOverTCP(ctx context.Context, conn net.Conn) (net.Conn, error) {
return &gvisorFakeUDPTunnelConn{ctx: ctx, Conn: conn}, nil
func newGvisorUDPConnOverTCP(ctx context.Context, conn net.Conn) (net.Conn, error) {
return &gvisorUDPConnOverTCP{ctx: ctx, Conn: conn}, nil
}
func (c *gvisorFakeUDPTunnelConn) Read(b []byte) (int, error) {
func (c *gvisorUDPConnOverTCP) Read(b []byte) (int, error) {
select {
case <-c.ctx.Done():
return 0, c.ctx.Err()
default:
dgram, err := readDatagramPacket(c.Conn, b)
datagram, err := readDatagramPacket(c.Conn, b)
if err != nil {
return 0, err
}
return int(dgram.DataLength), nil
return int(datagram.DataLength), nil
}
}
func (c *gvisorFakeUDPTunnelConn) Write(b []byte) (int, error) {
dgram := newDatagramPacket(b)
if err := dgram.Write(c.Conn); err != nil {
func (c *gvisorUDPConnOverTCP) Write(b []byte) (int, error) {
buf := config.LPool.Get().([]byte)[:]
n := copy(buf, b)
defer config.LPool.Put(buf)
packet := newDatagramPacket(buf, n)
if err := packet.Write(c.Conn); err != nil {
return 0, err
}
return len(b), nil
}
func (c *gvisorFakeUDPTunnelConn) Close() error {
func (c *gvisorUDPConnOverTCP) Close() error {
if cc, ok := c.Conn.(interface{ CloseRead() error }); ok {
_ = cc.CloseRead()
}
@@ -86,7 +100,7 @@ func (c *gvisorFakeUDPTunnelConn) Close() error {
}
func GvisorUDPListener(addr string) (net.Listener, error) {
plog.G(context.Background()).Debugf("Gvisor UDP over TCP listening addr: %s", addr)
plog.G(context.Background()).Infof("Gvisor UDP over TCP listening addr: %s", addr)
laddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
@@ -107,43 +121,32 @@ func handle(ctx context.Context, tcpConn net.Conn, udpConn *net.UDPConn) {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
for {
select {
case <-ctx.Done():
return
default:
}
for ctx.Err() == nil {
err := tcpConn.SetReadDeadline(time.Now().Add(time.Second * 30))
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to set read deadline: %v", err)
errChan <- err
errChan <- errors.WithMessage(err, "set read deadline failed")
return
}
dgram, err := readDatagramPacket(tcpConn, buf[:])
datagram, err := readDatagramPacket(tcpConn, buf)
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] %s -> %s: %v", tcpConn.RemoteAddr(), udpConn.LocalAddr(), err)
errChan <- err
errChan <- errors.WithMessage(err, "read datagram packet failed")
return
}
if dgram.DataLength == 0 {
plog.G(ctx).Errorf("[TUN-UDP] Length is zero")
if datagram.DataLength == 0 {
errChan <- fmt.Errorf("length of read packet is zero")
return
}
err = udpConn.SetWriteDeadline(time.Now().Add(time.Second * 30))
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to set write deadline: %v", err)
errChan <- err
errChan <- errors.WithMessage(err, "set write deadline failed")
return
}
if _, err = udpConn.Write(dgram.Data); err != nil {
plog.G(ctx).Errorf("[TUN-UDP] %s -> %s : %s", tcpConn.RemoteAddr(), "localhost:8422", err)
errChan <- err
if _, err = udpConn.Write(datagram.Data[:datagram.DataLength]); err != nil {
errChan <- errors.WithMessage(err, "write datagram packet failed")
return
}
plog.G(ctx).Debugf("[TUN-UDP] %s >>> %s length: %d", tcpConn.RemoteAddr(), "localhost:8422", dgram.DataLength)
plog.G(ctx).Debugf("[TUN-UDP] %s >>> %s length: %d", tcpConn.RemoteAddr(), udpConn.RemoteAddr(), datagram.DataLength)
}
}()
@@ -152,27 +155,18 @@ func handle(ctx context.Context, tcpConn net.Conn, udpConn *net.UDPConn) {
buf := config.LPool.Get().([]byte)[:]
defer config.LPool.Put(buf[:])
for {
select {
case <-ctx.Done():
return
default:
}
for ctx.Err() == nil {
err := udpConn.SetReadDeadline(time.Now().Add(time.Second * 30))
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Failed to set read deadline failed: %v", err)
errChan <- err
errChan <- errors.WithMessage(err, "set read deadline failed")
return
}
n, _, err := udpConn.ReadFrom(buf[:])
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] %s : %s", tcpConn.RemoteAddr(), err)
errChan <- err
errChan <- errors.WithMessage(err, "read datagram packet failed")
return
}
if n == 0 {
plog.G(ctx).Errorf("[TUN-UDP] Length is zero")
errChan <- fmt.Errorf("length of read packet is zero")
return
}
@@ -180,21 +174,19 @@ func handle(ctx context.Context, tcpConn net.Conn, udpConn *net.UDPConn) {
// pipe from peer to tunnel
err = tcpConn.SetWriteDeadline(time.Now().Add(time.Second * 30))
if err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Error: set write deadline failed: %v", err)
errChan <- errors.WithMessage(err, "set write deadline failed")
return
}
packet := newDatagramPacket(buf, n)
if err = packet.Write(tcpConn); err != nil {
errChan <- err
return
}
dgram := newDatagramPacket(buf[:n])
if err = dgram.Write(tcpConn); err != nil {
plog.G(ctx).Errorf("[TUN-UDP] Error: %s <- %s : %s", tcpConn.RemoteAddr(), dgram.Addr(), err)
errChan <- err
return
}
plog.G(ctx).Debugf("[TUN-UDP] %s <<< %s length: %d", tcpConn.RemoteAddr(), dgram.Addr(), len(dgram.Data))
plog.G(ctx).Debugf("[TUN-UDP] %s <<< %s length: %d", tcpConn.RemoteAddr(), tcpConn.LocalAddr(), packet.DataLength)
}
}()
err := <-errChan
if err != nil {
if err != nil && !errors.Is(err, io.EOF) {
plog.G(ctx).Errorf("[TUN-UDP] %v", err)
}
plog.G(ctx).Debugf("[TUN-UDP] %s >-< %s", tcpConn.RemoteAddr(), udpConn.LocalAddr())

View File

@@ -0,0 +1,69 @@
package core
import (
"context"
"net"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
)
var _ net.PacketConn = (*PacketConnOverTCP)(nil)
type PacketConnOverTCP struct {
// tcp connection
net.Conn
ctx context.Context
}
func NewPacketConnOverTCP(ctx context.Context, conn net.Conn) (net.Conn, error) {
return &PacketConnOverTCP{ctx: ctx, Conn: conn}, nil
}
func (c *PacketConnOverTCP) ReadFrom(b []byte) (int, net.Addr, error) {
select {
case <-c.ctx.Done():
return 0, nil, c.ctx.Err()
default:
datagram, err := readDatagramPacket(c.Conn, b)
if err != nil {
return 0, nil, err
}
return int(datagram.DataLength), nil, nil
}
}
func (c *PacketConnOverTCP) Read(b []byte) (int, error) {
n, _, err := c.ReadFrom(b)
return n, err
}
func (c *PacketConnOverTCP) WriteTo(b []byte, _ net.Addr) (int, error) {
if len(b) == 0 {
return 0, nil
}
buf := config.LPool.Get().([]byte)[:]
n := copy(buf, b)
defer config.LPool.Put(buf)
packet := newDatagramPacket(buf, n)
if err := packet.Write(c.Conn); err != nil {
return 0, err
}
return len(b), nil
}
func (c *PacketConnOverTCP) Write(b []byte) (int, error) {
n, err := c.WriteTo(b, nil)
return n, err
}
func (c *PacketConnOverTCP) Close() error {
if cc, ok := c.Conn.(interface{ CloseRead() error }); ok {
_ = cc.CloseRead()
}
if cc, ok := c.Conn.(interface{ CloseWrite() error }); ok {
_ = cc.CloseWrite()
}
return c.Conn.Close()
}

View File

@@ -4,14 +4,11 @@ import (
"context"
"fmt"
"net"
"os"
"strings"
"sync"
"github.com/containernetworking/cni/pkg/types"
"github.com/pkg/errors"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/tun"
)
@@ -20,101 +17,81 @@ var (
// RouteMapTCP map[srcIP]net.Conn Globe route table for inner ip
RouteMapTCP = &sync.Map{}
// TCPPacketChan tcp connects
TCPPacketChan = make(chan *datagramPacket, MaxSize)
TCPPacketChan = make(chan *Packet, MaxSize)
)
type TCPUDPacket struct {
data *datagramPacket
}
// Route example:
// -L "tcp://:10800" -L "tun://:8422?net=198.19.0.100/16"
// -L "tun:/10.233.24.133:8422?net=198.19.0.102/16&route=198.19.0.0/16"
// -L "tun:/127.0.0.1:8422?net=198.19.0.102/16&route=198.19.0.0/16,10.233.0.0/16" -F "tcp://127.0.0.1:10800"
// -l "gtcp://:10801" -l "tun://?net=198.19.0.100/16"
// -l "tun:/tcp://10.233.24.133:8422?net=198.19.0.102/16&route=198.19.0.0/16"
// -l "tun:/tcp://127.0.0.1:10800?net=198.19.0.102/16&route=198.19.0.0/16,10.233.0.0/16"
type Route struct {
ServeNodes []string // -L tun
ChainNode string // -F tcp
Retries int
Listeners []string // -l tun
Retries int
}
func (r *Route) parseChain() (*Chain, error) {
node, err := parseChainNode(r.ChainNode)
func ParseForwarder(remote string) (*Forwarder, error) {
forwarder, err := ParseNode(remote)
if err != nil {
return nil, err
}
return NewChain(r.Retries, node), nil
}
func parseChainNode(ns string) (*Node, error) {
node, err := ParseNode(ns)
if err != nil {
return nil, err
forwarder.Client = &Client{
Connector: NewUDPOverTCPConnector(),
Transporter: TCPTransporter(nil),
}
node.Client = &Client{
Connector: UDPOverTCPTunnelConnector(),
Transporter: TCPTransporter(),
}
return node, nil
return NewForwarder(5, forwarder), nil
}
func (r *Route) GenerateServers() ([]Server, error) {
chain, err := r.parseChain()
if err != nil && !errors.Is(err, ErrorInvalidNode) {
plog.G(context.Background()).Errorf("Failed to parse chain: %v", err)
return nil, err
}
servers := make([]Server, 0, len(r.ServeNodes))
for _, serveNode := range r.ServeNodes {
var node *Node
node, err = ParseNode(serveNode)
servers := make([]Server, 0, len(r.Listeners))
for _, l := range r.Listeners {
node, err := ParseNode(l)
if err != nil {
plog.G(context.Background()).Errorf("Failed to parse node %s: %v", serveNode, err)
plog.G(context.Background()).Errorf("Failed to parse node %s: %v", l, err)
return nil, err
}
var ln net.Listener
var listener net.Listener
var handler Handler
switch node.Protocol {
case "tun":
handler = TunHandler(chain, node)
ln, err = tun.Listener(tun.Config{
var forwarder *Forwarder
if node.Remote != "" {
forwarder, err = ParseForwarder(node.Remote)
if err != nil {
return nil, err
}
}
handler = TunHandler(node, forwarder)
listener, err = tun.Listener(tun.Config{
Name: node.Get("name"),
Addr: node.Get("net"),
Addr6: os.Getenv(config.EnvInboundPodTunIPv6),
Addr6: node.Get("net6"),
MTU: node.GetInt("mtu"),
Routes: parseIPRoutes(node.Get("route")),
Routes: parseRoutes(node.Get("route")),
Gateway: node.Get("gw"),
})
if err != nil {
plog.G(context.Background()).Errorf("Failed to create tun listener: %v", err)
return nil, err
}
case "tcp":
handler = TCPHandler()
ln, err = TCPListener(node.Addr)
if err != nil {
plog.G(context.Background()).Errorf("Failed to create tcp listener: %v", err)
return nil, err
}
case "gtcp":
handler = GvisorTCPHandler()
ln, err = GvisorTCPListener(node.Addr)
listener, err = GvisorTCPListener(node.Addr)
if err != nil {
plog.G(context.Background()).Errorf("Failed to create gvisor tcp listener: %v", err)
return nil, err
}
case "gudp":
handler = GvisorUDPHandler()
ln, err = GvisorUDPListener(node.Addr)
listener, err = GvisorUDPListener(node.Addr)
if err != nil {
plog.G(context.Background()).Errorf("Failed to create gvisor udp listener: %v", err)
return nil, err
}
case "ssh":
handler = SSHHandler()
ln, err = SSHListener(node.Addr)
listener, err = SSHListener(node.Addr)
if err != nil {
plog.G(context.Background()).Errorf("Failed to create ssh listener: %v", err)
return nil, err
@@ -123,21 +100,18 @@ func (r *Route) GenerateServers() ([]Server, error) {
plog.G(context.Background()).Errorf("Not support protocol %s", node.Protocol)
return nil, fmt.Errorf("not support protocol %s", node.Protocol)
}
servers = append(servers, Server{Listener: ln, Handler: handler})
servers = append(servers, Server{Listener: listener, Handler: handler})
}
return servers, nil
}
func parseIPRoutes(routeStringList string) (routes []types.Route) {
if len(routeStringList) == 0 {
return
}
routeList := strings.Split(routeStringList, ",")
for _, route := range routeList {
func parseRoutes(str string) []types.Route {
var routes []types.Route
list := strings.Split(str, ",")
for _, route := range list {
if _, ipNet, _ := net.ParseCIDR(strings.TrimSpace(route)); ipNet != nil {
routes = append(routes, types.Route{Dst: *ipNet})
}
}
return
return routes
}

View File

@@ -18,7 +18,7 @@ func SSHListener(addr string) (net.Listener, error) {
if err != nil {
return nil, err
}
plog.G(context.Background()).Debugf("starting ssh server on port %s...", addr)
plog.G(context.Background()).Infof("starting ssh server on port %s...", addr)
return ln, err
}
@@ -38,7 +38,7 @@ func (s *sshHandler) Handle(ctx context.Context, conn net.Conn) {
}),
Handler: ssh.Handler(func(s ssh.Session) {
io.WriteString(s, "Remote forwarding available...\n")
select {}
<-s.Context().Done()
}),
ReversePortForwardingCallback: ssh.ReversePortForwardingCallback(func(ctx ssh.Context, host string, port uint32) bool {
plog.G(ctx).Infoln("attempt to bind", host, port, "granted")

View File

@@ -2,32 +2,44 @@ package core
import (
"context"
"crypto/tls"
"errors"
"net"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
type tcpTransporter struct{}
type tcpTransporter struct {
tlsConfig *tls.Config
}
func TCPTransporter() Transporter {
return &tcpTransporter{}
func TCPTransporter(tlsInfo map[string][]byte) Transporter {
tlsConfig, err := util.GetTlsClientConfig(tlsInfo)
if err != nil {
if errors.Is(err, util.ErrNoTLSConfig) {
plog.G(context.Background()).Warn("tls config not found in config, use raw tcp mode")
return &tcpTransporter{}
}
plog.G(context.Background()).Errorf("failed to get tls client config: %v", err)
return &tcpTransporter{}
}
return &tcpTransporter{tlsConfig: tlsConfig}
}
func (tr *tcpTransporter) Dial(ctx context.Context, addr string) (net.Conn, error) {
dialer := &net.Dialer{Timeout: config.DialTimeout}
return dialer.DialContext(ctx, "tcp", addr)
}
func TCPListener(addr string) (net.Listener, error) {
laddr, err := net.ResolveTCPAddr("tcp", addr)
dialer := &net.Dialer{Timeout: config.DialTimeout, KeepAlive: config.KeepAliveTime}
conn, err := dialer.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
}
ln, err := net.ListenTCP("tcp", laddr)
if err != nil {
return nil, err
if tr.tlsConfig == nil {
plog.G(ctx).Debugf("tls config not found in config, use raw tcp mode")
return conn, nil
}
return &tcpKeepAliveListener{TCPListener: ln}, nil
plog.G(ctx).Debugf("Use tls mode")
return tls.Client(conn, tr.tlsConfig), nil
}
type tcpKeepAliveListener struct {

View File

@@ -3,23 +3,18 @@ package core
import (
"context"
"net"
"strings"
"sync"
"time"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
)
type fakeUDPTunnelConnector struct {
type UDPOverTCPConnector struct {
}
func UDPOverTCPTunnelConnector() Connector {
return &fakeUDPTunnelConnector{}
func NewUDPOverTCPConnector() Connector {
return &UDPOverTCPConnector{}
}
func (c *fakeUDPTunnelConnector) ConnectContext(ctx context.Context, conn net.Conn) (net.Conn, error) {
func (c *UDPOverTCPConnector) ConnectContext(ctx context.Context, conn net.Conn) (net.Conn, error) {
//defer conn.SetDeadline(time.Time{})
switch con := conn.(type) {
case *net.TCPConn:
@@ -31,118 +26,10 @@ func (c *fakeUDPTunnelConnector) ConnectContext(ctx context.Context, conn net.Co
if err != nil {
return nil, err
}
err = con.SetKeepAlivePeriod(15 * time.Second)
err = con.SetKeepAlivePeriod(config.KeepAliveTime)
if err != nil {
return nil, err
}
}
return newFakeUDPTunnelConnOverTCP(ctx, conn)
}
type fakeUdpHandler struct {
// map[srcIP]net.Conn
routeMapTCP *sync.Map
packetChan chan *datagramPacket
}
func TCPHandler() Handler {
return &fakeUdpHandler{
routeMapTCP: RouteMapTCP,
packetChan: TCPPacketChan,
}
}
func (h *fakeUdpHandler) Handle(ctx context.Context, tcpConn net.Conn) {
defer tcpConn.Close()
plog.G(ctx).Debugf("[TCP] %s -> %s", tcpConn.RemoteAddr(), tcpConn.LocalAddr())
defer func(addr net.Addr) {
var keys []string
h.routeMapTCP.Range(func(key, value any) bool {
if value.(net.Conn) == tcpConn {
keys = append(keys, key.(string))
}
return true
})
for _, key := range keys {
h.routeMapTCP.Delete(key)
}
plog.G(ctx).Debugf("[TCP] To %s by conn %s from globle route map TCP", strings.Join(keys, " "), addr)
}(tcpConn.LocalAddr())
for {
select {
case <-ctx.Done():
return
default:
}
buf := config.LPool.Get().([]byte)[:]
dgram, err := readDatagramPacketServer(tcpConn, buf[:])
if err != nil {
plog.G(ctx).Errorf("[TCP] %s -> %s : %v", tcpConn.RemoteAddr(), tcpConn.LocalAddr(), err)
config.LPool.Put(buf[:])
return
}
var src net.IP
src, _, err = util.ParseIP(dgram.Data[:dgram.DataLength])
if err != nil {
plog.G(ctx).Errorf("[TCP] Unknown packet")
config.LPool.Put(buf[:])
continue
}
value, loaded := h.routeMapTCP.LoadOrStore(src.String(), tcpConn)
if loaded {
if tcpConn != value.(net.Conn) {
h.routeMapTCP.Store(src.String(), tcpConn)
plog.G(ctx).Debugf("[TCP] Replace route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
}
} else {
plog.G(ctx).Debugf("[TCP] Add new route map TCP: %s -> %s-%s", src, tcpConn.LocalAddr(), tcpConn.RemoteAddr())
}
util.SafeWrite(h.packetChan, dgram)
}
}
// fake udp connect over tcp
type fakeUDPTunnelConn struct {
// tcp connection
net.Conn
ctx context.Context
}
func newFakeUDPTunnelConnOverTCP(ctx context.Context, conn net.Conn) (net.Conn, error) {
return &fakeUDPTunnelConn{ctx: ctx, Conn: conn}, nil
}
func (c *fakeUDPTunnelConn) ReadFrom(b []byte) (int, net.Addr, error) {
select {
case <-c.ctx.Done():
return 0, nil, c.ctx.Err()
default:
dgram, err := readDatagramPacket(c.Conn, b)
if err != nil {
return 0, nil, err
}
return int(dgram.DataLength), dgram.Addr(), nil
}
}
func (c *fakeUDPTunnelConn) WriteTo(b []byte, _ net.Addr) (int, error) {
dgram := newDatagramPacket(b)
if err := dgram.Write(c.Conn); err != nil {
return 0, err
}
return len(b), nil
}
func (c *fakeUDPTunnelConn) Close() error {
if cc, ok := c.Conn.(interface{ CloseRead() error }); ok {
_ = cc.CloseRead()
}
if cc, ok := c.Conn.(interface{ CloseWrite() error }); ok {
_ = cc.CloseWrite()
}
return c.Conn.Close()
return NewUDPConnOverTCP(ctx, conn)
}

View File

@@ -4,7 +4,8 @@ import (
"context"
"net"
"sync"
"time"
"github.com/google/gopacket/layers"
"github.com/wencaiwulue/kubevpn/v2/pkg/config"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
@@ -16,176 +17,51 @@ const (
)
type tunHandler struct {
chain *Chain
forward *Forwarder
node *Node
routeMapUDP *RouteMap
// map[srcIP]net.Conn
routeMapTCP *sync.Map
chExit chan error
}
type RouteMap struct {
lock *sync.RWMutex
routes map[string]net.Addr
}
func NewRouteMap() *RouteMap {
return &RouteMap{
lock: &sync.RWMutex{},
routes: map[string]net.Addr{},
}
}
func (n *RouteMap) LoadOrStore(to net.IP, addr net.Addr) (net.Addr, bool) {
n.lock.RLock()
route, load := n.routes[to.String()]
n.lock.RUnlock()
if load {
return route, true
}
n.lock.Lock()
defer n.lock.Unlock()
n.routes[to.String()] = addr
return addr, false
}
func (n *RouteMap) Store(to net.IP, addr net.Addr) {
n.lock.Lock()
defer n.lock.Unlock()
n.routes[to.String()] = addr
}
func (n *RouteMap) RouteTo(ip net.IP) net.Addr {
n.lock.RLock()
defer n.lock.RUnlock()
return n.routes[ip.String()]
errChan chan error
}
// TunHandler creates a handler for tun tunnel.
func TunHandler(chain *Chain, node *Node) Handler {
func TunHandler(node *Node, forward *Forwarder) Handler {
return &tunHandler{
chain: chain,
node: node,
routeMapUDP: NewRouteMap(),
forward: forward,
routeMapTCP: RouteMapTCP,
chExit: make(chan error, 1),
errChan: make(chan error, 1),
}
}
func (h *tunHandler) Handle(ctx context.Context, tun net.Conn) {
if h.node.Remote != "" {
h.HandleClient(ctx, tun)
tunIfi, err := util.GetTunDeviceByConn(tun)
if err != nil {
plog.G(ctx).Errorf("Failed to get tun device: %v", err)
return
}
ctx = plog.WithField(ctx, tunIfi.Name, "")
if !h.forward.IsEmpty() {
h.HandleClient(ctx, tun, h.forward)
} else {
h.HandleServer(ctx, tun)
}
}
type Device struct {
tun net.Conn
tunInbound chan *DataElem
tunOutbound chan *DataElem
// your main logic
tunInboundHandler func(tunInbound <-chan *DataElem, tunOutbound chan<- *DataElem)
chExit chan error
}
func (d *Device) readFromTun() {
defer util.HandleCrash()
for {
buf := config.LPool.Get().([]byte)[:]
n, err := d.tun.Read(buf[:])
if err != nil {
config.LPool.Put(buf[:])
plog.G(context.Background()).Errorf("[TUN] Failed to read from tun: %v", err)
util.SafeWrite(d.chExit, err)
return
}
if n == 0 {
plog.G(context.Background()).Errorf("[TUN] Read packet length 0")
config.LPool.Put(buf[:])
continue
}
src, dst, err := util.ParseIP(buf[:n])
if err != nil {
plog.G(context.Background()).Errorf("[TUN] Unknown packet")
config.LPool.Put(buf[:])
continue
}
plog.G(context.Background()).Debugf("[TUN] SRC: %s --> DST: %s, length: %d", src, dst, n)
util.SafeWrite(d.tunInbound, &DataElem{
data: buf[:],
length: n,
src: src,
dst: dst,
})
}
}
func (d *Device) writeToTun() {
defer util.HandleCrash()
for e := range d.tunOutbound {
_, err := d.tun.Write(e.data[:e.length])
config.LPool.Put(e.data[:])
if err != nil {
util.SafeWrite(d.chExit, err)
return
}
}
}
func (d *Device) Close() {
d.tun.Close()
util.SafeClose(d.tunInbound)
util.SafeClose(d.tunOutbound)
util.SafeClose(TCPPacketChan)
}
func heartbeats(ctx context.Context, tun net.Conn) {
tunIfi, err := util.GetTunDeviceByConn(tun)
if err != nil {
plog.G(ctx).Errorf("Failed to get tun device: %s", err.Error())
return
}
srcIPv4, srcIPv6, dockerSrcIPv4, err := util.GetTunDeviceIP(tunIfi.Name)
if err != nil {
return
func (h *tunHandler) HandleServer(ctx context.Context, tun net.Conn) {
device := &Device{
tun: tun,
tunInbound: make(chan *Packet, MaxSize),
tunOutbound: make(chan *Packet, MaxSize),
errChan: h.errChan,
}
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
for ; true; <-ticker.C {
select {
case <-ctx.Done():
return
default:
}
if srcIPv4 != nil {
go util.Ping(ctx, srcIPv4.String(), config.RouterIP.String())
}
if srcIPv6 != nil {
go util.Ping(ctx, srcIPv6.String(), config.RouterIP6.String())
}
if dockerSrcIPv4 != nil {
go util.Ping(ctx, dockerSrcIPv4.String(), config.DockerRouterIP.String())
}
}
}
func (d *Device) Start(ctx context.Context) {
go d.readFromTun()
go d.tunInboundHandler(d.tunInbound, d.tunOutbound)
go d.writeToTun()
defer device.Close()
go device.readFromTun(ctx)
go device.writeToTun(ctx)
go device.handlePacket(ctx, h.routeMapTCP)
select {
case err := <-d.chExit:
case err := <-device.errChan:
plog.G(ctx).Errorf("Device exit: %v", err)
return
case <-ctx.Done():
@@ -193,44 +69,95 @@ func (d *Device) Start(ctx context.Context) {
}
}
func (d *Device) SetTunInboundHandler(handler func(tunInbound <-chan *DataElem, tunOutbound chan<- *DataElem)) {
d.tunInboundHandler = handler
type Device struct {
tun net.Conn
tunInbound chan *Packet
tunOutbound chan *Packet
errChan chan error
}
func (h *tunHandler) HandleServer(ctx context.Context, tun net.Conn) {
device := &Device{
tun: tun,
tunInbound: make(chan *DataElem, MaxSize),
tunOutbound: make(chan *DataElem, MaxSize),
chExit: h.chExit,
func (d *Device) readFromTun(ctx context.Context) {
defer util.HandleCrash()
for ctx.Err() == nil {
buf := config.LPool.Get().([]byte)[:]
n, err := d.tun.Read(buf[:])
if err != nil {
config.LPool.Put(buf[:])
plog.G(ctx).Errorf("[TUN] Failed to read from tun device: %v", err)
util.SafeWrite(d.errChan, err)
return
}
src, dst, protocol, err := util.ParseIP(buf[:n])
if err != nil {
plog.G(ctx).Errorf("[TUN] Unknown packet")
config.LPool.Put(buf[:])
continue
}
plog.G(ctx).Debugf("[TUN] SRC: %s, DST: %s, Protocol: %s, Length: %d", src, dst, layers.IPProtocol(protocol).String(), n)
d.tunInbound <- NewPacket(buf[:], n, src, dst)
}
device.SetTunInboundHandler(func(tunInbound <-chan *DataElem, tunOutbound chan<- *DataElem) {
for ctx.Err() == nil {
packetConn, err := (&net.ListenConfig{}).ListenPacket(ctx, "udp", h.node.Addr)
if err != nil {
plog.G(ctx).Errorf("[UDP] Failed to listen %s: %v", h.node.Addr, err)
}
func (d *Device) writeToTun(ctx context.Context) {
defer util.HandleCrash()
for ctx.Err() == nil {
var packet *Packet
select {
case packet = <-d.tunOutbound:
if packet == nil {
return
}
err = transportTunServer(ctx, tunInbound, tunOutbound, packetConn, h.routeMapUDP, h.routeMapTCP)
if err != nil {
plog.G(ctx).Errorf("[TUN] %s: %v", tun.LocalAddr(), err)
}
case <-ctx.Done():
return
}
})
defer device.Close()
device.Start(ctx)
_, err := d.tun.Write(packet.data[1:packet.length])
config.LPool.Put(packet.data[:])
if err != nil {
plog.G(ctx).Errorf("[TUN] Failed to write to tun device: %v", err)
util.SafeWrite(d.errChan, err)
return
}
}
}
type DataElem struct {
func (d *Device) Close() {
d.tun.Close()
}
func (d *Device) handlePacket(ctx context.Context, routeMapTCP *sync.Map) {
p := &Peer{
tunInbound: d.tunInbound,
tunOutbound: d.tunOutbound,
routeMapTCP: routeMapTCP,
errChan: make(chan error, 1),
}
go p.routeTun(ctx)
go p.routeTCPToTun(ctx)
select {
case err := <-p.errChan:
plog.G(ctx).Errorf("[TUN] %s: %v", d.tun.LocalAddr(), err)
util.SafeWrite(d.errChan, err)
return
case <-ctx.Done():
return
}
}
type Packet struct {
data []byte
length int
src net.IP
dst net.IP
}
func NewDataElem(data []byte, length int, src net.IP, dst net.IP) *DataElem {
return &DataElem{
func NewPacket(data []byte, length int, src net.IP, dst net.IP) *Packet {
return &Packet{
data: data,
length: length,
src: src,
@@ -238,32 +165,18 @@ func NewDataElem(data []byte, length int, src net.IP, dst net.IP) *DataElem {
}
}
func (d *DataElem) Data() []byte {
func (d *Packet) Data() []byte {
return d.data
}
func (d *DataElem) Length() int {
func (d *Packet) Length() int {
return d.length
}
type udpElem struct {
from net.Addr
data []byte
length int
src net.IP
dst net.IP
}
type Peer struct {
conn net.PacketConn
tunInbound chan *Packet
tunOutbound chan<- *Packet
connInbound chan *udpElem
tunInbound <-chan *DataElem
tunOutbound chan<- *DataElem
// map[srcIP.String()]net.Addr for udp
routeMapUDP *RouteMap
// map[srcIP.String()]net.Conn for tcp
routeMapTCP *sync.Map
@@ -277,154 +190,49 @@ func (p *Peer) sendErr(err error) {
}
}
func (p *Peer) readFromConn() {
func (p *Peer) routeTCPToTun(ctx context.Context) {
defer util.HandleCrash()
for {
buf := config.LPool.Get().([]byte)[:]
n, from, err := p.conn.ReadFrom(buf[:])
if err != nil {
config.LPool.Put(buf[:])
p.sendErr(err)
for ctx.Err() == nil {
var packet *Packet
select {
case packet = <-TCPPacketChan:
if packet == nil {
return
}
case <-ctx.Done():
return
}
src, dst, err := util.ParseIP(buf[:n])
if err != nil {
config.LPool.Put(buf[:])
plog.G(context.Background()).Errorf("[TUN] Unknown packet: %v", err)
continue
}
if addr, loaded := p.routeMapUDP.LoadOrStore(src, from); loaded {
if addr.String() != from.String() {
p.routeMapUDP.Store(src, from)
plog.G(context.Background()).Debugf("[TUN] Replace route map UDP: %s -> %s", src, from)
}
} else {
plog.G(context.Background()).Debugf("[TUN] Add new route map UDP: %s -> %s", src, from)
}
p.connInbound <- &udpElem{
from: from,
data: buf[:],
length: n,
src: src,
dst: dst,
}
p.tunOutbound <- packet
}
}
func (p *Peer) readFromTCPConn() {
func (p *Peer) routeTun(ctx context.Context) {
defer util.HandleCrash()
for packet := range TCPPacketChan {
src, dst, err := util.ParseIP(packet.Data)
if err != nil {
plog.G(context.Background()).Errorf("[TUN] Unknown packet")
config.LPool.Put(packet.Data[:])
continue
}
u := &udpElem{
data: packet.Data[:],
length: int(packet.DataLength),
src: src,
dst: dst,
}
plog.G(context.Background()).Debugf("[TCP] udp-tun %s >>> %s length: %d", u.src, u.dst, u.length)
p.connInbound <- u
}
}
func (p *Peer) routePeer() {
defer util.HandleCrash()
for e := range p.connInbound {
if routeToAddr := p.routeMapUDP.RouteTo(e.dst); routeToAddr != nil {
plog.G(context.Background()).Debugf("[UDP] Find UDP route to dst: %s -> %s", e.dst, routeToAddr)
_, err := p.conn.WriteTo(e.data[:e.length], routeToAddr)
config.LPool.Put(e.data[:])
if err != nil {
p.sendErr(err)
for ctx.Err() == nil {
var packet *Packet
select {
case packet = <-p.tunInbound:
if packet == nil {
return
}
} else if conn, ok := p.routeMapTCP.Load(e.dst.String()); ok {
plog.G(context.Background()).Debugf("[TCP] Find TCP route to dst: %s -> %s", e.dst.String(), conn.(net.Conn).RemoteAddr())
dgram := newDatagramPacket(e.data[:e.length])
case <-ctx.Done():
return
}
if conn, ok := p.routeMapTCP.Load(packet.dst.String()); ok {
plog.G(ctx).Debugf("[TUN] Find TCP route to dst: %s -> %s", packet.dst.String(), conn.(net.Conn).RemoteAddr())
copy(packet.data[1:packet.length+1], packet.data[:packet.length])
packet.data[0] = 1
dgram := newDatagramPacket(packet.data, packet.length+1)
err := dgram.Write(conn.(net.Conn))
config.LPool.Put(e.data[:])
config.LPool.Put(packet.data[:])
if err != nil {
plog.G(context.Background()).Errorf("[TCP] udp-tun %s <- %s : %s", conn.(net.Conn).RemoteAddr(), dgram.Addr(), err)
plog.G(ctx).Errorf("[TUN] Failed to write TCP %s <- %s : %s", conn.(net.Conn).RemoteAddr(), conn.(net.Conn).LocalAddr(), err)
p.sendErr(err)
return
}
} else {
plog.G(context.Background()).Debugf("[TUN] Not found route to dst: %s, write to TUN device", e.dst.String())
p.tunOutbound <- &DataElem{
data: e.data,
length: e.length,
src: e.src,
dst: e.dst,
}
plog.G(ctx).Warnf("[TUN] No route for src: %s -> dst: %s, drop it", packet.src, packet.dst)
config.LPool.Put(packet.data[:])
}
}
}
func (p *Peer) routeTUN() {
defer util.HandleCrash()
for e := range p.tunInbound {
if addr := p.routeMapUDP.RouteTo(e.dst); addr != nil {
plog.G(context.Background()).Debugf("[TUN] Find UDP route to dst: %s -> %s", e.dst, addr)
_, err := p.conn.WriteTo(e.data[:e.length], addr)
config.LPool.Put(e.data[:])
if err != nil {
plog.G(context.Background()).Debugf("[TUN] Failed wirte to route dst: %s -> %s", e.dst, addr)
p.sendErr(err)
return
}
} else if conn, ok := p.routeMapTCP.Load(e.dst.String()); ok {
plog.G(context.Background()).Debugf("[TUN] Find TCP route to dst: %s -> %s", e.dst.String(), conn.(net.Conn).RemoteAddr())
dgram := newDatagramPacket(e.data[:e.length])
err := dgram.Write(conn.(net.Conn))
config.LPool.Put(e.data[:])
if err != nil {
plog.G(context.Background()).Errorf("[TUN] Failed to write TCP %s <- %s : %s", conn.(net.Conn).RemoteAddr(), dgram.Addr(), err)
p.sendErr(err)
return
}
} else {
plog.G(context.Background()).Errorf("[TUN] No route for src: %s -> dst: %s, drop it", e.src, e.dst)
config.LPool.Put(e.data[:])
}
}
}
func (p *Peer) Start() {
go p.readFromConn()
go p.readFromTCPConn()
go p.routePeer()
go p.routeTUN()
}
func (p *Peer) Close() {
p.conn.Close()
}
func transportTunServer(ctx context.Context, tunInbound <-chan *DataElem, tunOutbound chan<- *DataElem, packetConn net.PacketConn, routeMapUDP *RouteMap, routeMapTCP *sync.Map) error {
p := &Peer{
conn: packetConn,
connInbound: make(chan *udpElem, MaxSize),
tunInbound: tunInbound,
tunOutbound: tunOutbound,
routeMapUDP: routeMapUDP,
routeMapTCP: routeMapTCP,
errChan: make(chan error, 2),
}
defer p.Close()
p.Start()
select {
case err := <-p.errChan:
plog.G(ctx).Errorf(err.Error())
return err
case <-ctx.Done():
return nil
}
}

Some files were not shown because too many files have changed in this diff Show More