mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-12-24 11:51:13 +08:00
Compare commits
239 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbd5e121e5 | ||
|
|
1fe1e29f72 | ||
|
|
2dad0ef766 | ||
|
|
c45f5be4ea | ||
|
|
29d4b6d314 | ||
|
|
903487b209 | ||
|
|
abaa9e474e | ||
|
|
871fec10c3 | ||
|
|
577ce07c37 | ||
|
|
0eafa3402c | ||
|
|
b2a9d9fc2a | ||
|
|
e85b8b7451 | ||
|
|
cc1f08033e | ||
|
|
b0bcc16f5f | ||
|
|
e830a28581 | ||
|
|
cf5f0b4e88 | ||
|
|
66eca5fdce | ||
|
|
51e4b90f74 | ||
|
|
811ac6d615 | ||
|
|
abfd4e9b86 | ||
|
|
adad81e7a4 | ||
|
|
61349e778f | ||
|
|
cd7236da1b | ||
|
|
f6d23566d2 | ||
|
|
00bcef1e49 | ||
|
|
f4a0ab39f4 | ||
|
|
a7ebb10696 | ||
|
|
c62134a5eb | ||
|
|
4fe94cfd0c | ||
|
|
dd2d0f5c65 | ||
|
|
14026ea493 | ||
|
|
79fcc1b8f5 | ||
|
|
cf473d85c9 | ||
|
|
2d23a46d33 | ||
|
|
0548c5a8a0 | ||
|
|
3a4dbe41c7 | ||
|
|
10079a541f | ||
|
|
98c4a61ca1 | ||
|
|
4df63d1642 | ||
|
|
4ddba64737 | ||
|
|
aef13e4c74 | ||
|
|
6e45edb077 | ||
|
|
47b2f0006c | ||
|
|
f59aa8aca1 | ||
|
|
1b5ff03463 | ||
|
|
d40e69e781 | ||
|
|
d2a6d78331 | ||
|
|
5b0758be69 | ||
|
|
9fb5d01f28 | ||
|
|
4ddff991bc | ||
|
|
faecc95653 | ||
|
|
7411deab75 | ||
|
|
1b8b6f8390 | ||
|
|
6197138ad6 | ||
|
|
a13540a258 | ||
|
|
3a45a33fb9 | ||
|
|
d49a0dc3e5 | ||
|
|
b3b13fce86 | ||
|
|
2d5653ee2b | ||
|
|
c4d28fd497 | ||
|
|
6a82b12646 | ||
|
|
b2c7fb07d3 | ||
|
|
05905bb8ba | ||
|
|
38584da9d3 | ||
|
|
5a97a5d6e2 | ||
|
|
1823567949 | ||
|
|
43023d080f | ||
|
|
9cd8b32c6b | ||
|
|
4e568fe9e3 | ||
|
|
31ead176c6 | ||
|
|
620a14f066 | ||
|
|
211c9309b2 | ||
|
|
62725d8011 | ||
|
|
8eb646dfb8 | ||
|
|
e490f72a78 | ||
|
|
61a33ff5bd | ||
|
|
0cdb530cbd | ||
|
|
b5235a3a26 | ||
|
|
f14d074417 | ||
|
|
4021480ad5 | ||
|
|
4593ddcf24 | ||
|
|
480471efc5 | ||
|
|
edd2450880 | ||
|
|
b3af39f840 | ||
|
|
9010d05198 | ||
|
|
de9bbcd1b0 | ||
|
|
0fa136cb7a | ||
|
|
306ba9be8f | ||
|
|
6ca22822f9 | ||
|
|
33fba01904 | ||
|
|
05be5c317e | ||
|
|
46d15c5742 | ||
|
|
4949df56ef | ||
|
|
072e67ce6c | ||
|
|
11bb5584ea | ||
|
|
bbbcebd9d5 | ||
|
|
423254b172 | ||
|
|
507da8a44c | ||
|
|
bfed866c04 | ||
|
|
bdfa4f6d16 | ||
|
|
5785ba0f42 | ||
|
|
1e6475379f | ||
|
|
bb991fc1d7 | ||
|
|
4f3e443bca | ||
|
|
8d64dab79f | ||
|
|
2614669671 | ||
|
|
2f16b31eb6 | ||
|
|
bdeb3d6d09 | ||
|
|
ab2a8d1821 | ||
|
|
fd59ed242c | ||
|
|
a750327d9e | ||
|
|
69c5ed6c98 | ||
|
|
d1dd44be35 | ||
|
|
229eb747a4 | ||
|
|
dd3ba1c059 | ||
|
|
db5ea99133 | ||
|
|
fcbe2d64f7 | ||
|
|
a0c0860051 | ||
|
|
e374d6b51d | ||
|
|
9703a12bc2 | ||
|
|
f9f52d1001 | ||
|
|
51c16989fe | ||
|
|
75c609211b | ||
|
|
6d545dc5c9 | ||
|
|
b17da3cbcb | ||
|
|
d1108ebd86 | ||
|
|
792839a2d4 | ||
|
|
f493931b41 | ||
|
|
7df065ef93 | ||
|
|
c265b3581c | ||
|
|
f802e03d01 | ||
|
|
c08cb461dd | ||
|
|
1a2649a02a | ||
|
|
facd6bdb3d | ||
|
|
a1117dee62 | ||
|
|
b28eaef6a7 | ||
|
|
46aebef01f | ||
|
|
3791f48737 | ||
|
|
fc76b70713 | ||
|
|
e990dc1d0f | ||
|
|
d636449073 | ||
|
|
e85e1a6c40 | ||
|
|
40d09716c4 | ||
|
|
63792172bd | ||
|
|
ca6a2be70f | ||
|
|
e21fc8cda9 | ||
|
|
1f4698c6f8 | ||
|
|
efea780edf | ||
|
|
bdb21f8964 | ||
|
|
e33d2f1928 | ||
|
|
e6df115933 | ||
|
|
549e56cd05 | ||
|
|
54ed2b711f | ||
|
|
56b81574ac | ||
|
|
ce2b7a010e | ||
|
|
5df0c3ffdc | ||
|
|
8b0e87592a | ||
|
|
31a42c1fa7 | ||
|
|
ee0957a5c9 | ||
|
|
206d74c331 | ||
|
|
53ed72dee3 | ||
|
|
323235f268 | ||
|
|
6af6622bd3 | ||
|
|
18ef72fc20 | ||
|
|
fe08448249 | ||
|
|
ebaa4098f1 | ||
|
|
9ba873494f | ||
|
|
da40f3315b | ||
|
|
c4540b1930 | ||
|
|
a6ec321e46 | ||
|
|
79f8aca7df | ||
|
|
6edfc3127d | ||
|
|
bed0a9168c | ||
|
|
d5ee35bfa8 | ||
|
|
9661a122bd | ||
|
|
28657e3832 | ||
|
|
6a8a197f48 | ||
|
|
31186fc1d9 | ||
|
|
fca3baf47e | ||
|
|
1cae5d270b | ||
|
|
a3556a263d | ||
|
|
dd80717d8d | ||
|
|
537b2940fe | ||
|
|
9aae88d54b | ||
|
|
100a8df723 | ||
|
|
48e30b4344 | ||
|
|
c9f1ce6522 | ||
|
|
c42e3475f9 | ||
|
|
4fb338b5fc | ||
|
|
15243b3935 | ||
|
|
f0f9459976 | ||
|
|
ee7d5fa6f9 | ||
|
|
e393f8371e | ||
|
|
ca333fcdaf | ||
|
|
7e4e9e1e0d | ||
|
|
58ee2df1a3 | ||
|
|
15200f1caf | ||
|
|
23baab449c | ||
|
|
0ddcaa8acc | ||
|
|
0c122473ce | ||
|
|
d08f74a57e | ||
|
|
cd66bb7907 | ||
|
|
f303616554 | ||
|
|
3973b85d25 | ||
|
|
4fd1f014bd | ||
|
|
fe62cf6c4d | ||
|
|
c5900d070c | ||
|
|
d84ca66cfb | ||
|
|
60c3030e65 | ||
|
|
ea574a756b | ||
|
|
e8735a68be | ||
|
|
d55d290677 | ||
|
|
45435bcc48 | ||
|
|
dbe9f91ee0 | ||
|
|
b3d2e1e838 | ||
|
|
fa0b343401 | ||
|
|
a1bb338cdb | ||
|
|
dbc9df070b | ||
|
|
804708aabe | ||
|
|
21087fc708 | ||
|
|
94db7846d8 | ||
|
|
e205b77e41 | ||
|
|
2927261390 | ||
|
|
8f37488207 | ||
|
|
d05a53a77f | ||
|
|
a2df9f7b59 | ||
|
|
cd68b1fb00 | ||
|
|
208f607f03 | ||
|
|
116a1f1983 | ||
|
|
d191c927f4 | ||
|
|
a030dc582b | ||
|
|
08bcbe1611 | ||
|
|
fb428403a2 | ||
|
|
4f4bbd79f2 | ||
|
|
1ec3ca4637 | ||
|
|
484a5cafe4 | ||
|
|
b62a6b0185 | ||
|
|
90898c8047 | ||
|
|
c06daf68e8 |
28
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
Normal file
28
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
Normal 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
47
.github/ISSUE_TEMPLATE/02-bug.yml
vendored
Normal 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
|
||||
5
.github/workflows/coverage.yml
vendored
5
.github/workflows/coverage.yml
vendored
@@ -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
|
||||
|
||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -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:
|
||||
|
||||
39
.github/workflows/test.yml
vendored
39
.github/workflows/test.yml
vendored
@@ -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:
|
||||
|
||||
7
Makefile
7
Makefile
@@ -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
|
||||
|
||||
84
README.md
84
README.md
@@ -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
|
||||
|
||||
[](https://jb.gg/OpenSourceSupport)
|
||||
[](https://jb.gg/OpenSourceSupport)
|
||||
|
||||
### [Donate](https://kubevpn.dev/docs/donate)
|
||||
86
README_ZH.md
86
README_ZH.md
@@ -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
|
||||
### 支持者
|
||||
|
||||
[](https://jb.gg/OpenSourceSupport)
|
||||
|
||||
### [捐赠支持](https://kubevpn.dev/zh/docs/donate/)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
36
charts/kubevpn/README.md
Normal 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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 }}"
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
98
cmd/kubevpn/cmds/connection.go
Normal file
98
cmd/kubevpn/cmds/connection.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
26
cmd/kubevpn/cmds/once.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
146
cmd/kubevpn/cmds/route.go
Normal 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
148
cmd/kubevpn/cmds/run.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
59
cmd/kubevpn/cmds/server.go
Normal file
59
cmd/kubevpn/cmds/server.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
153
cmd/kubevpn/cmds/sync.go
Normal 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
|
||||
}
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
63
cmd/kubevpn/cmds/unsync.go
Normal file
63
cmd/kubevpn/cmds/unsync.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
266
go.mod
@@ -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
|
||||
)
|
||||
|
||||
815
go.work.sum
Normal file
815
go.work.sum
Normal 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=
|
||||
@@ -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"
|
||||
/*
|
||||
reason:docker 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 !"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
74
pkg/core/bufferedtcp.go
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
96
pkg/core/forwarder.go
Normal 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
|
||||
}
|
||||
@@ -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
104
pkg/core/gvisorlocalstack.go
Executable 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
|
||||
}
|
||||
84
pkg/core/gvisorlocaltcpforwarder.go
Normal file
84
pkg/core/gvisorlocaltcpforwarder.go
Normal 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
|
||||
}
|
||||
60
pkg/core/gvisorlocaltcphandler.go
Normal file
60
pkg/core/gvisorlocaltcphandler.go
Normal 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
|
||||
}
|
||||
}
|
||||
70
pkg/core/gvisorlocaltunendpoint.go
Executable file
70
pkg/core/gvisorlocaltunendpoint.go
Executable 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()
|
||||
}
|
||||
}
|
||||
127
pkg/core/gvisorlocaludpforwarder.go
Normal file
127
pkg/core/gvisorlocaludpforwarder.go
Normal 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
|
||||
}
|
||||
@@ -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{
|
||||
{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
69
pkg/core/packetconnovertcp.go
Normal file
69
pkg/core/packetconnovertcp.go
Normal 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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user