Compare commits
66 Commits
kiloio-git
...
0.4.0
Author | SHA1 | Date | |
---|---|---|---|
|
0dfb744630 | ||
|
d95e590f5c | ||
|
d3710399f8 | ||
|
0eb9df178a | ||
|
e782d1be98 | ||
|
fb03520fb5 | ||
|
ed1e9ea400 | ||
|
df8d2cb68f | ||
|
38a5dd22e9 | ||
|
e598102f04 | ||
|
5de689ea1f | ||
|
887ea026bb | ||
|
75fb31a947 | ||
|
a1af9790ea | ||
|
96029a584f | ||
|
3bf7eacc7e | ||
|
6d6c62ae49 | ||
|
02d49ded39 | ||
|
3e7fe47131 | ||
|
038a6d7450 | ||
|
c4e3108549 | ||
|
6a696e03e7 | ||
|
797133f272 | ||
|
84da98c2b1 | ||
|
76047fe0af | ||
|
ee650342d5 | ||
|
1f8c736ba4 | ||
|
57a89b49ff | ||
|
6a5643287e | ||
|
e1a6ee9e2c | ||
|
ee480dece4 | ||
|
05e8ded744 | ||
|
ac65330c71 | ||
|
8a2c82267c | ||
|
fb70091169 | ||
|
f03a0bb247 | ||
|
bb3554a3c6 | ||
|
edb8f63848 | ||
|
bcb722b0b9 | ||
|
70b7eb52fa | ||
|
c59ac10e15 | ||
|
584a8bf13d | ||
|
b88ca7f8cd | ||
|
8f7894e598 | ||
|
3de4bf527b | ||
|
f90288133d | ||
|
70d2751030 | ||
|
9b14c227a9 | ||
|
e2745b453f | ||
|
a6eef5a8cf | ||
|
3174467751 | ||
|
df8d1aba5c | ||
|
c099a70c20 | ||
|
79e96bbe37 | ||
|
b9823943e3 | ||
|
c8ed21cac4 | ||
|
6b93cc2ad9 | ||
|
086b2e1ddd | ||
|
2b4487ba9a | ||
|
cad15d9961 | ||
|
9ec155b843 | ||
|
e886f5d24e | ||
|
acc3696057 | ||
|
288bb824aa | ||
|
6fe0beabcd | ||
|
0fbd33788e |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
**
|
||||||
|
|
||||||
|
!/bin/linux
|
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
@@ -13,6 +13,19 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
vendor:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.17.1
|
||||||
|
- name: Vendor
|
||||||
|
run: |
|
||||||
|
make vendor
|
||||||
|
git diff --exit-code
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -20,7 +33,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make
|
run: make
|
||||||
|
|
||||||
@@ -31,7 +44,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Build kg and kgctl for all Linux Architectures
|
- name: Build kg and kgctl for all Linux Architectures
|
||||||
run: make all-build
|
run: make all-build
|
||||||
|
|
||||||
@@ -42,7 +55,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Build kgctl for Darwin amd64
|
- name: Build kgctl for Darwin amd64
|
||||||
run: make OS=darwin ARCH=amd64
|
run: make OS=darwin ARCH=amd64
|
||||||
- name: Build kgctl for Darwin arm64
|
- name: Build kgctl for Darwin arm64
|
||||||
@@ -55,7 +68,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Build kgctl for Windows
|
- name: Build kgctl for Windows
|
||||||
run: make OS=windows
|
run: make OS=windows
|
||||||
|
|
||||||
@@ -66,7 +79,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Run Unit Tests
|
- name: Run Unit Tests
|
||||||
run: make unit
|
run: make unit
|
||||||
|
|
||||||
@@ -78,7 +91,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Run e2e Tests
|
- name: Run e2e Tests
|
||||||
run: make e2e
|
run: make e2e
|
||||||
|
|
||||||
@@ -89,7 +102,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Lint Code
|
- name: Lint Code
|
||||||
run: make lint
|
run: make lint
|
||||||
|
|
||||||
@@ -100,7 +113,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Enable Experimental Docker CLI
|
- name: Enable Experimental Docker CLI
|
||||||
run: |
|
run: |
|
||||||
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
||||||
@@ -116,6 +129,7 @@ jobs:
|
|||||||
push:
|
push:
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
needs:
|
needs:
|
||||||
|
- vendor
|
||||||
- build
|
- build
|
||||||
- linux
|
- linux
|
||||||
- darwin
|
- darwin
|
||||||
@@ -129,7 +143,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Enable Experimental Docker CLI
|
- name: Enable Experimental Docker CLI
|
||||||
run: |
|
run: |
|
||||||
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
||||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16.5
|
go-version: 1.17.1
|
||||||
- name: Make Directory with kgctl Binaries to Be Released
|
- name: Make Directory with kgctl Binaries to Be Released
|
||||||
run: make release
|
run: make release
|
||||||
- name: Publish Release
|
- name: Publish Release
|
||||||
|
@@ -11,7 +11,7 @@ ARG GOARCH
|
|||||||
ARG ALPINE_VERSION=v3.12
|
ARG ALPINE_VERSION=v3.12
|
||||||
LABEL maintainer="squat <lserven@gmail.com>"
|
LABEL maintainer="squat <lserven@gmail.com>"
|
||||||
RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \
|
RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \
|
||||||
apk add --no-cache ipset iptables ip6tables wireguard-tools graphviz font-noto
|
apk add --no-cache ipset iptables ip6tables graphviz font-noto
|
||||||
COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/
|
COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/
|
||||||
COPY bin/linux/$GOARCH/kg /opt/bin/
|
COPY bin/linux/$GOARCH/kg /opt/bin/
|
||||||
ENTRYPOINT ["/opt/bin/kg"]
|
ENTRYPOINT ["/opt/bin/kg"]
|
||||||
|
8
Makefile
8
Makefile
@@ -45,8 +45,8 @@ KUBECTL_BINARY := $(shell pwd)/bin/kubectl
|
|||||||
BASH_UNIT := $(shell pwd)/bin/bash_unit
|
BASH_UNIT := $(shell pwd)/bin/bash_unit
|
||||||
BASH_UNIT_FLAGS :=
|
BASH_UNIT_FLAGS :=
|
||||||
|
|
||||||
BUILD_IMAGE ?= golang:1.16.5-alpine
|
BUILD_IMAGE ?= golang:1.17.1-alpine3.14
|
||||||
BASE_IMAGE ?= alpine:3.13
|
BASE_IMAGE ?= alpine:3.14
|
||||||
|
|
||||||
build: $(BINS)
|
build: $(BINS)
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ $(BASH_UNIT):
|
|||||||
chmod +x $@
|
chmod +x $@
|
||||||
|
|
||||||
e2e: container $(KIND_BINARY) $(KUBECTL_BINARY) $(BASH_UNIT) bin/$(OS)/$(ARCH)/kgctl
|
e2e: container $(KIND_BINARY) $(KUBECTL_BINARY) $(BASH_UNIT) bin/$(OS)/$(ARCH)/kgctl
|
||||||
KILO_IMAGE=$(IMAGE):$(ARCH)-$(VERSION) KIND_BINARY=$(KIND_BINARY) KUBECTL_BINARY=$(KUBECTL_BINARY) KGCTL_BINARY=$(shell pwd)/bin/$(OS)/$(ARCH)/kgctl $(BASH_UNIT) $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/multi-cluster.sh ./e2e/handlers.sh ./e2e/teardown.sh
|
KILO_IMAGE=$(IMAGE):$(ARCH)-$(VERSION) KIND_BINARY=$(KIND_BINARY) KUBECTL_BINARY=$(KUBECTL_BINARY) KGCTL_BINARY=$(shell pwd)/bin/$(OS)/$(ARCH)/kgctl $(BASH_UNIT) $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/multi-cluster.sh ./e2e/handlers.sh ./e2e/kgctl.sh ./e2e/teardown.sh
|
||||||
|
|
||||||
header: .header
|
header: .header
|
||||||
@HEADER=$$(cat .header); \
|
@HEADER=$$(cat .header); \
|
||||||
@@ -242,7 +242,7 @@ website/docs/README.md: README.md
|
|||||||
cat README.md >> $@
|
cat README.md >> $@
|
||||||
cp -r docs/graphs website/static/img/
|
cp -r docs/graphs website/static/img/
|
||||||
sed -i 's/\.\/docs\///g' $@
|
sed -i 's/\.\/docs\///g' $@
|
||||||
find $(@D) -type f -name '*.md' | xargs -I{} sed -i 's/\.\/\(.\+\.svg\)/\/img\/\1/g' {}
|
find $(@D) -type f -name '*.md' | xargs -I{} sed -i 's/\.\/\(.\+\.\(svg\|png\)\)/\/img\/\1/g' {}
|
||||||
sed -i 's/graphs\//\/img\/graphs\//g' $@
|
sed -i 's/graphs\//\/img\/graphs\//g' $@
|
||||||
# The next line is a workaround until mdx, docusaurus' markdown parser, can parse links with preceding brackets.
|
# The next line is a workaround until mdx, docusaurus' markdown parser, can parse links with preceding brackets.
|
||||||
sed -i 's/\[\]\(\[.*\](.*)\)/\[\]\1/g' website/docs/api.md
|
sed -i 's/\[\]\(\[.*\](.*)\)/\[\]\1/g' website/docs/api.md
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/mesh"
|
"github.com/squat/kilo/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,7 +64,7 @@ func (h *graphHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
peers[p.Name] = p
|
peers[p.Name] = p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
topo, err := mesh.NewTopology(nodes, peers, h.granularity, *h.hostname, 0, []byte{}, h.subnet, nodes[*h.hostname].PersistentKeepalive, nil)
|
topo, err := mesh.NewTopology(nodes, peers, h.granularity, *h.hostname, 0, wgtypes.Key{}, h.subnet, nodes[*h.hostname].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, fmt.Sprintf("failed to create topology: %v", err), http.StatusInternalServerError)
|
http.Error(w, fmt.Sprintf("failed to create topology: %v", err), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
194
cmd/kg/main.go
194
cmd/kg/main.go
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -29,8 +29,9 @@ import (
|
|||||||
"github.com/go-kit/kit/log/level"
|
"github.com/go-kit/kit/log/level"
|
||||||
"github.com/oklog/run"
|
"github.com/oklog/run"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
flag "github.com/spf13/pflag"
|
"github.com/spf13/cobra"
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@@ -78,52 +79,79 @@ var (
|
|||||||
}, ", ")
|
}, ", ")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Main is the principal function for the binary, wrapped only by `main` for convenience.
|
var cmd = &cobra.Command{
|
||||||
func Main() error {
|
Use: "kg",
|
||||||
backend := flag.String("backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
Short: "kg is the Kilo agent",
|
||||||
cleanUpIface := flag.Bool("clean-up-interface", false, "Should Kilo delete its interface when it shuts down?")
|
Long: `kg is the Kilo agent.
|
||||||
createIface := flag.Bool("create-interface", true, "Should kilo create an interface on startup?")
|
It runs on every node of a cluster,
|
||||||
cni := flag.Bool("cni", true, "Should Kilo manage the node's CNI configuration?")
|
setting up the public and private keys for the VPN
|
||||||
cniPath := flag.String("cni-path", mesh.DefaultCNIPath, "Path to CNI config.")
|
as well as the necessary rules to route packets between locations.`,
|
||||||
compatibility := flag.String("compatibility", "", fmt.Sprintf("Should Kilo run in compatibility mode? Possible values: %s", availableCompatibilities))
|
PreRunE: preRun,
|
||||||
encapsulate := flag.String("encapsulate", string(encapsulation.Always), fmt.Sprintf("When should Kilo encapsulate packets within a location? Possible values: %s", availableEncapsulations))
|
RunE: runRoot,
|
||||||
granularity := flag.String("mesh-granularity", string(mesh.LogicalGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
SilenceUsage: true,
|
||||||
kubeconfig := flag.String("kubeconfig", "", "Path to kubeconfig.")
|
SilenceErrors: true,
|
||||||
hostname := flag.String("hostname", "", "Hostname of the node on which this process is running.")
|
}
|
||||||
iface := flag.String("interface", mesh.DefaultKiloInterface, "Name of the Kilo interface to use; if it does not exist, it will be created.")
|
|
||||||
listen := flag.String("listen", ":1107", "The address at which to listen for health and metrics.")
|
|
||||||
local := flag.Bool("local", true, "Should Kilo manage routes within a location?")
|
|
||||||
logLevel := flag.String("log-level", logLevelInfo, fmt.Sprintf("Log level to use. Possible values: %s", availableLogLevels))
|
|
||||||
master := flag.String("master", "", "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
|
||||||
mtu := flag.Uint("mtu", wireguard.DefaultMTU, "The MTU of the WireGuard interface created by Kilo.")
|
|
||||||
topologyLabel := flag.String("topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
|
||||||
var port uint
|
|
||||||
flag.UintVar(&port, "port", mesh.DefaultKiloPort, "The port over which WireGuard peers should communicate.")
|
|
||||||
subnet := flag.String("subnet", mesh.DefaultKiloSubnet.String(), "CIDR from which to allocate addresses for WireGuard interfaces.")
|
|
||||||
resyncPeriod := flag.Duration("resync-period", 30*time.Second, "How often should the Kilo controllers reconcile?")
|
|
||||||
printVersion := flag.Bool("version", false, "Print version and exit")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *printVersion {
|
var (
|
||||||
fmt.Println(version.Version)
|
backend string
|
||||||
return nil
|
cleanUpIface bool
|
||||||
}
|
createIface bool
|
||||||
|
cni bool
|
||||||
|
cniPath string
|
||||||
|
compatibility string
|
||||||
|
encapsulate string
|
||||||
|
granularity string
|
||||||
|
hostname string
|
||||||
|
kubeconfig string
|
||||||
|
iface string
|
||||||
|
listen string
|
||||||
|
local bool
|
||||||
|
master string
|
||||||
|
mtu uint
|
||||||
|
topologyLabel string
|
||||||
|
port int
|
||||||
|
subnet string
|
||||||
|
resyncPeriod time.Duration
|
||||||
|
iptablesForwardRule bool
|
||||||
|
prioritisePrivateAddr bool
|
||||||
|
|
||||||
_, s, err := net.ParseCIDR(*subnet)
|
printVersion bool
|
||||||
if err != nil {
|
logLevel string
|
||||||
return fmt.Errorf("failed to parse %q as CIDR: %v", *subnet, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *hostname == "" {
|
logger log.Logger
|
||||||
var err error
|
registry *prometheus.Registry
|
||||||
*hostname, err = os.Hostname()
|
)
|
||||||
if *hostname == "" || err != nil {
|
|
||||||
return errors.New("failed to determine hostname")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
|
func init() {
|
||||||
switch *logLevel {
|
cmd.Flags().StringVar(&backend, "backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
||||||
|
cmd.Flags().BoolVar(&cleanUpIface, "clean-up-interface", false, "Should Kilo delete its interface when it shuts down?")
|
||||||
|
cmd.Flags().BoolVar(&createIface, "create-interface", true, "Should kilo create an interface on startup?")
|
||||||
|
cmd.Flags().BoolVar(&cni, "cni", true, "Should Kilo manage the node's CNI configuration?")
|
||||||
|
cmd.Flags().StringVar(&cniPath, "cni-path", mesh.DefaultCNIPath, "Path to CNI config.")
|
||||||
|
cmd.Flags().StringVar(&compatibility, "compatibility", "", fmt.Sprintf("Should Kilo run in compatibility mode? Possible values: %s", availableCompatibilities))
|
||||||
|
cmd.Flags().StringVar(&encapsulate, "encapsulate", string(encapsulation.Always), fmt.Sprintf("When should Kilo encapsulate packets within a location? Possible values: %s", availableEncapsulations))
|
||||||
|
cmd.Flags().StringVar(&granularity, "mesh-granularity", string(mesh.LogicalGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
||||||
|
cmd.Flags().StringVar(&kubeconfig, "kubeconfig", "", "Path to kubeconfig.")
|
||||||
|
cmd.Flags().StringVar(&hostname, "hostname", "", "Hostname of the node on which this process is running.")
|
||||||
|
cmd.Flags().StringVar(&iface, "interface", mesh.DefaultKiloInterface, "Name of the Kilo interface to use; if it does not exist, it will be created.")
|
||||||
|
cmd.Flags().StringVar(&listen, "listen", ":1107", "The address at which to listen for health and metrics.")
|
||||||
|
cmd.Flags().BoolVar(&local, "local", true, "Should Kilo manage routes within a location?")
|
||||||
|
cmd.Flags().StringVar(&master, "master", "", "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
||||||
|
cmd.Flags().UintVar(&mtu, "mtu", wireguard.DefaultMTU, "The MTU of the WireGuard interface created by Kilo.")
|
||||||
|
cmd.Flags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
||||||
|
cmd.Flags().IntVar(&port, "port", mesh.DefaultKiloPort, "The port over which WireGuard peers should communicate.")
|
||||||
|
cmd.Flags().StringVar(&subnet, "subnet", mesh.DefaultKiloSubnet.String(), "CIDR from which to allocate addresses for WireGuard interfaces.")
|
||||||
|
cmd.Flags().DurationVar(&resyncPeriod, "resync-period", 30*time.Second, "How often should the Kilo controllers reconcile?")
|
||||||
|
cmd.Flags().BoolVar(&iptablesForwardRule, "iptables-forward-rules", false, "Add default accept rules to the FORWARD chain in iptables. Warning: this may break firewalls with a deny all policy and is potentially insecure!")
|
||||||
|
cmd.Flags().BoolVar(&prioritisePrivateAddr, "prioritise-private-addresses", false, "Prefer to assign a private IP address to the node's endpoint.")
|
||||||
|
|
||||||
|
cmd.PersistentFlags().BoolVar(&printVersion, "version", false, "Print version and exit")
|
||||||
|
cmd.PersistentFlags().StringVar(&logLevel, "log-level", logLevelInfo, fmt.Sprintf("Log level to use. Possible values: %s", availableLogLevels))
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRun(_ *cobra.Command, _ []string) error {
|
||||||
|
logger = log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
switch logLevel {
|
||||||
case logLevelAll:
|
case logLevelAll:
|
||||||
logger = level.NewFilter(logger, level.AllowAll())
|
logger = level.NewFilter(logger, level.AllowAll())
|
||||||
case logLevelDebug:
|
case logLevelDebug:
|
||||||
@@ -137,73 +165,100 @@ func Main() error {
|
|||||||
case logLevelNone:
|
case logLevelNone:
|
||||||
logger = level.NewFilter(logger, level.AllowNone())
|
logger = level.NewFilter(logger, level.AllowNone())
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("log level %v unknown; possible values are: %s", *logLevel, availableLogLevels)
|
return fmt.Errorf("log level %v unknown; possible values are: %s", logLevel, availableLogLevels)
|
||||||
}
|
}
|
||||||
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
|
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
|
||||||
logger = log.With(logger, "caller", log.DefaultCaller)
|
logger = log.With(logger, "caller", log.DefaultCaller)
|
||||||
|
|
||||||
e := encapsulation.Strategy(*encapsulate)
|
registry = prometheus.NewRegistry()
|
||||||
|
registry.MustRegister(
|
||||||
|
collectors.NewGoCollector(),
|
||||||
|
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// runRoot is the principal function for the binary.
|
||||||
|
func runRoot(_ *cobra.Command, _ []string) error {
|
||||||
|
if printVersion {
|
||||||
|
fmt.Println(version.Version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, s, err := net.ParseCIDR(subnet)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse %q as CIDR: %v", subnet, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostname == "" {
|
||||||
|
var err error
|
||||||
|
hostname, err = os.Hostname()
|
||||||
|
if hostname == "" || err != nil {
|
||||||
|
return errors.New("failed to determine hostname")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e := encapsulation.Strategy(encapsulate)
|
||||||
switch e {
|
switch e {
|
||||||
case encapsulation.Never:
|
case encapsulation.Never:
|
||||||
case encapsulation.CrossSubnet:
|
case encapsulation.CrossSubnet:
|
||||||
case encapsulation.Always:
|
case encapsulation.Always:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("encapsulation %v unknown; possible values are: %s", *encapsulate, availableEncapsulations)
|
return fmt.Errorf("encapsulation %v unknown; possible values are: %s", encapsulate, availableEncapsulations)
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc encapsulation.Encapsulator
|
var enc encapsulation.Encapsulator
|
||||||
switch *compatibility {
|
switch compatibility {
|
||||||
case "flannel":
|
case "flannel":
|
||||||
enc = encapsulation.NewFlannel(e)
|
enc = encapsulation.NewFlannel(e)
|
||||||
default:
|
default:
|
||||||
enc = encapsulation.NewIPIP(e)
|
enc = encapsulation.NewIPIP(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
gr := mesh.Granularity(*granularity)
|
gr := mesh.Granularity(granularity)
|
||||||
switch gr {
|
switch gr {
|
||||||
case mesh.LogicalGranularity:
|
case mesh.LogicalGranularity:
|
||||||
case mesh.FullGranularity:
|
case mesh.FullGranularity:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("mesh granularity %v unknown; possible values are: %s", *granularity, availableGranularities)
|
return fmt.Errorf("mesh granularity %v unknown; possible values are: %s", granularity, availableGranularities)
|
||||||
}
|
}
|
||||||
|
|
||||||
var b mesh.Backend
|
var b mesh.Backend
|
||||||
switch *backend {
|
switch backend {
|
||||||
case k8s.Backend:
|
case k8s.Backend:
|
||||||
config, err := clientcmd.BuildConfigFromFlags(*master, *kubeconfig)
|
config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create Kubernetes config: %v", err)
|
return fmt.Errorf("failed to create Kubernetes config: %v", err)
|
||||||
}
|
}
|
||||||
c := kubernetes.NewForConfigOrDie(config)
|
c := kubernetes.NewForConfigOrDie(config)
|
||||||
kc := kiloclient.NewForConfigOrDie(config)
|
kc := kiloclient.NewForConfigOrDie(config)
|
||||||
ec := apiextensions.NewForConfigOrDie(config)
|
ec := apiextensions.NewForConfigOrDie(config)
|
||||||
b = k8s.New(c, kc, ec, *topologyLabel)
|
b = k8s.New(c, kc, ec, topologyLabel, log.With(logger, "component", "k8s backend"))
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("backend %v unknown; possible values are: %s", *backend, availableBackends)
|
return fmt.Errorf("backend %v unknown; possible values are: %s", backend, availableBackends)
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := mesh.New(b, enc, gr, *hostname, uint32(port), s, *local, *cni, *cniPath, *iface, *cleanUpIface, *createIface, *mtu, *resyncPeriod, log.With(logger, "component", "kilo"))
|
if port < 1 || port > 1<<16-1 {
|
||||||
|
return fmt.Errorf("invalid port: port mus be in range [%d:%d], but got %d", 1, 1<<16-1, port)
|
||||||
|
}
|
||||||
|
m, err := mesh.New(b, enc, gr, hostname, port, s, local, cni, cniPath, iface, cleanUpIface, createIface, mtu, resyncPeriod, prioritisePrivateAddr, iptablesForwardRule, log.With(logger, "component", "kilo"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create Kilo mesh: %v", err)
|
return fmt.Errorf("failed to create Kilo mesh: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := prometheus.NewRegistry()
|
m.RegisterMetrics(registry)
|
||||||
r.MustRegister(
|
|
||||||
prometheus.NewGoCollector(),
|
|
||||||
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
|
|
||||||
)
|
|
||||||
m.RegisterMetrics(r)
|
|
||||||
|
|
||||||
var g run.Group
|
var g run.Group
|
||||||
{
|
{
|
||||||
// Run the HTTP server.
|
// Run the HTTP server.
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/health", healthHandler)
|
mux.HandleFunc("/health", healthHandler)
|
||||||
mux.Handle("/graph", &graphHandler{m, gr, hostname, s})
|
mux.Handle("/graph", &graphHandler{m, gr, &hostname, s})
|
||||||
mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
|
mux.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
|
||||||
l, err := net.Listen("tcp", *listen)
|
l, err := net.Listen("tcp", listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to listen on %s: %v", *listen, err)
|
return fmt.Errorf("failed to listen on %s: %v", listen, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Add(func() error {
|
g.Add(func() error {
|
||||||
@@ -252,8 +307,15 @@ func Main() error {
|
|||||||
return g.Run()
|
return g.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print the version and exit.",
|
||||||
|
Run: func(_ *cobra.Command, _ []string) { fmt.Println(version.Version) },
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := Main(); err != nil {
|
cmd.AddCommand(webhookCmd, versionCmd)
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
273
cmd/kg/webhook.go
Normal file
273
cmd/kg/webhook.go
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// Copyright 2021 the Kilo authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log/level"
|
||||||
|
"github.com/oklog/run"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
v1 "k8s.io/api/admission/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
|
||||||
|
kilo "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
|
"github.com/squat/kilo/pkg/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var webhookCmd = &cobra.Command{
|
||||||
|
Use: "webhook",
|
||||||
|
PreRunE: func(c *cobra.Command, a []string) error {
|
||||||
|
if c.HasParent() {
|
||||||
|
return c.Parent().PreRunE(c, a)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Short: "webhook starts a HTTPS server to validate updates and creations of Kilo peers.",
|
||||||
|
RunE: webhook,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
certPath string
|
||||||
|
keyPath string
|
||||||
|
metricsAddr string
|
||||||
|
listenAddr string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
webhookCmd.Flags().StringVar(&certPath, "cert-file", "", "The path to a certificate file")
|
||||||
|
webhookCmd.Flags().StringVar(&keyPath, "key-file", "", "The path to a key file")
|
||||||
|
webhookCmd.Flags().StringVar(&metricsAddr, "listen-metrics", ":1107", "The metrics server will be listening to that address")
|
||||||
|
webhookCmd.Flags().StringVar(&listenAddr, "listen", ":8443", "The webhook server will be listening to that address")
|
||||||
|
}
|
||||||
|
|
||||||
|
var deserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer()
|
||||||
|
|
||||||
|
var (
|
||||||
|
validationCounter = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "admission_requests_total",
|
||||||
|
Help: "The number of received admission reviews requests",
|
||||||
|
},
|
||||||
|
[]string{"operation", "response"},
|
||||||
|
)
|
||||||
|
requestCounter = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "http_requests_total",
|
||||||
|
Help: "The number of received http requests",
|
||||||
|
},
|
||||||
|
[]string{"handler", "method"},
|
||||||
|
)
|
||||||
|
errorCounter = prometheus.NewCounter(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "errors_total",
|
||||||
|
Help: "The total number of errors",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func validationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
level.Debug(logger).Log("msg", "handling request", "source", r.RemoteAddr)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
errorCounter.Inc()
|
||||||
|
level.Error(logger).Log("err", "failed to parse body from incoming request", "source", r.RemoteAddr)
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var admissionReview v1.AdmissionReview
|
||||||
|
|
||||||
|
contentType := r.Header.Get("Content-Type")
|
||||||
|
if contentType != "application/json" {
|
||||||
|
errorCounter.Inc()
|
||||||
|
msg := fmt.Sprintf("received Content-Type=%s, expected application/json", contentType)
|
||||||
|
level.Error(logger).Log("err", msg)
|
||||||
|
http.Error(w, msg, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := v1.AdmissionReview{}
|
||||||
|
|
||||||
|
_, gvk, err := deserializer.Decode(body, nil, &admissionReview)
|
||||||
|
if err != nil {
|
||||||
|
errorCounter.Inc()
|
||||||
|
msg := fmt.Sprintf("Request could not be decoded: %v", err)
|
||||||
|
level.Error(logger).Log("err", msg)
|
||||||
|
http.Error(w, msg, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *gvk != v1.SchemeGroupVersion.WithKind("AdmissionReview") {
|
||||||
|
errorCounter.Inc()
|
||||||
|
msg := "only API v1 is supported"
|
||||||
|
level.Error(logger).Log("err", msg)
|
||||||
|
http.Error(w, msg, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.SetGroupVersionKind(*gvk)
|
||||||
|
response.Response = &v1.AdmissionResponse{
|
||||||
|
UID: admissionReview.Request.UID,
|
||||||
|
}
|
||||||
|
|
||||||
|
rawExtension := admissionReview.Request.Object
|
||||||
|
var peer kilo.Peer
|
||||||
|
|
||||||
|
if err := json.Unmarshal(rawExtension.Raw, &peer); err != nil {
|
||||||
|
errorCounter.Inc()
|
||||||
|
msg := fmt.Sprintf("could not unmarshal extension to peer spec: %v:", err)
|
||||||
|
level.Error(logger).Log("err", msg)
|
||||||
|
http.Error(w, msg, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := peer.Validate(); err == nil {
|
||||||
|
level.Debug(logger).Log("msg", "got valid peer spec", "spec", peer.Spec, "name", peer.ObjectMeta.Name)
|
||||||
|
validationCounter.With(prometheus.Labels{"operation": string(admissionReview.Request.Operation), "response": "allowed"}).Inc()
|
||||||
|
response.Response.Allowed = true
|
||||||
|
} else {
|
||||||
|
level.Debug(logger).Log("msg", "got invalid peer spec", "spec", peer.Spec, "name", peer.ObjectMeta.Name)
|
||||||
|
validationCounter.With(prometheus.Labels{"operation": string(admissionReview.Request.Operation), "response": "denied"}).Inc()
|
||||||
|
response.Response.Result = &metav1.Status{
|
||||||
|
Message: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
errorCounter.Inc()
|
||||||
|
msg := fmt.Sprintf("failed to marshal response: %v", err)
|
||||||
|
level.Error(logger).Log("err", msg)
|
||||||
|
http.Error(w, msg, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if _, err := w.Write(res); err != nil {
|
||||||
|
level.Error(logger).Log("err", err, "msg", "failed to write response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func metricsMiddleWare(path string, next func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestCounter.With(prometheus.Labels{"method": r.Method, "handler": path}).Inc()
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webhook(_ *cobra.Command, _ []string) error {
|
||||||
|
if printVersion {
|
||||||
|
fmt.Println(version.Version)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
registry.MustRegister(
|
||||||
|
errorCounter,
|
||||||
|
validationCounter,
|
||||||
|
requestCounter,
|
||||||
|
)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
var g run.Group
|
||||||
|
g.Add(run.SignalHandler(ctx, syscall.SIGINT, syscall.SIGTERM))
|
||||||
|
{
|
||||||
|
mm := http.NewServeMux()
|
||||||
|
mm.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
|
||||||
|
msrv := &http.Server{
|
||||||
|
Addr: metricsAddr,
|
||||||
|
Handler: mm,
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Add(
|
||||||
|
func() error {
|
||||||
|
level.Info(logger).Log("msg", "starting metrics server", "address", msrv.Addr)
|
||||||
|
err := msrv.ListenAndServe()
|
||||||
|
level.Info(logger).Log("msg", "metrics server exited", "err", err)
|
||||||
|
return err
|
||||||
|
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
level.Info(logger).Log("msg", "received signal", "signal", serr.Signal.String(), "err", err.Error())
|
||||||
|
} else {
|
||||||
|
level.Error(logger).Log("msg", "received error", "err", err.Error())
|
||||||
|
}
|
||||||
|
level.Info(logger).Log("msg", "shutting down metrics server gracefully")
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
if err := msrv.Shutdown(ctx); err != nil {
|
||||||
|
level.Error(logger).Log("msg", "failed to shut down metrics server gracefully", "err", err.Error())
|
||||||
|
msrv.Close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/validate", metricsMiddleWare("/validate", validationHandler))
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: listenAddr,
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
g.Add(
|
||||||
|
func() error {
|
||||||
|
level.Info(logger).Log("msg", "starting webhook server", "address", srv.Addr)
|
||||||
|
err := srv.ListenAndServeTLS(certPath, keyPath)
|
||||||
|
level.Info(logger).Log("msg", "webhook server exited", "err", err)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
level.Info(logger).Log("msg", "received signal", "signal", serr.Signal.String(), "err", err.Error())
|
||||||
|
} else {
|
||||||
|
level.Error(logger).Log("msg", "received error", "err", err.Error())
|
||||||
|
}
|
||||||
|
level.Info(logger).Log("msg", "shutting down webhook server gracefully")
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
|
level.Error(logger).Log("msg", "failed to shut down webhook server gracefully", "err", err.Error())
|
||||||
|
srv.Close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := g.Run()
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
374
cmd/kgctl/connect_linux.go
Normal file
374
cmd/kgctl/connect_linux.go
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
// Copyright 2022 the Kilo authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/go-kit/kit/log/level"
|
||||||
|
"github.com/oklog/run"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/squat/kilo/pkg/iproute"
|
||||||
|
"github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
|
"github.com/squat/kilo/pkg/mesh"
|
||||||
|
"github.com/squat/kilo/pkg/route"
|
||||||
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logLevel string
|
||||||
|
connectOpts struct {
|
||||||
|
allowedIP net.IPNet
|
||||||
|
allowedIPs []net.IPNet
|
||||||
|
privateKey string
|
||||||
|
cleanUp bool
|
||||||
|
mtu uint
|
||||||
|
resyncPeriod time.Duration
|
||||||
|
interfaceName string
|
||||||
|
persistentKeepalive int
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func takeIPNet(_ net.IP, i *net.IPNet, err error) *net.IPNet {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "connect",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: runConnect,
|
||||||
|
Short: "connect to a Kilo cluster as a peer over WireGuard",
|
||||||
|
SilenceUsage: true,
|
||||||
|
}
|
||||||
|
cmd.Flags().IPNetVarP(&connectOpts.allowedIP, "allowed-ip", "a", *takeIPNet(net.ParseCIDR("10.10.10.10/32")), "Allowed IP of the peer.")
|
||||||
|
cmd.Flags().StringSliceVar(&allowedIPs, "allowed-ips", []string{}, "Additional allowed IPs of the cluster, e.g. the service CIDR.")
|
||||||
|
cmd.Flags().StringVar(&logLevel, "log-level", logLevelInfo, fmt.Sprintf("Log level to use. Possible values: %s", availableLogLevels))
|
||||||
|
cmd.Flags().StringVar(&connectOpts.privateKey, "private-key", "", "Path to an existing WireGuard private key file.")
|
||||||
|
cmd.Flags().BoolVar(&connectOpts.cleanUp, "clean-up", true, "Should Kilo clean up the routes and interface when it shuts down?")
|
||||||
|
cmd.Flags().UintVar(&connectOpts.mtu, "mtu", uint(1420), "The MTU for the WireGuard interface.")
|
||||||
|
cmd.Flags().DurationVar(&connectOpts.resyncPeriod, "resync-period", 30*time.Second, "How often should Kilo reconcile?")
|
||||||
|
cmd.Flags().StringVarP(&connectOpts.interfaceName, "interface", "i", mesh.DefaultKiloInterface, "Name of the Kilo interface to use; if it does not exist, it will be created.")
|
||||||
|
cmd.Flags().IntVar(&connectOpts.persistentKeepalive, "persistent-keepalive", 10, "How often should WireGuard send keepalives? Setting to 0 will disable sending keepalives.")
|
||||||
|
|
||||||
|
availableLogLevels = strings.Join([]string{
|
||||||
|
logLevelAll,
|
||||||
|
logLevelDebug,
|
||||||
|
logLevelInfo,
|
||||||
|
logLevelWarn,
|
||||||
|
logLevelError,
|
||||||
|
logLevelNone,
|
||||||
|
}, ", ")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func runConnect(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
logger := log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
switch logLevel {
|
||||||
|
case logLevelAll:
|
||||||
|
logger = level.NewFilter(logger, level.AllowAll())
|
||||||
|
case logLevelDebug:
|
||||||
|
logger = level.NewFilter(logger, level.AllowDebug())
|
||||||
|
case logLevelInfo:
|
||||||
|
logger = level.NewFilter(logger, level.AllowInfo())
|
||||||
|
case logLevelWarn:
|
||||||
|
logger = level.NewFilter(logger, level.AllowWarn())
|
||||||
|
case logLevelError:
|
||||||
|
logger = level.NewFilter(logger, level.AllowError())
|
||||||
|
case logLevelNone:
|
||||||
|
logger = level.NewFilter(logger, level.AllowNone())
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("log level %s unknown; possible values are: %s", logLevel, availableLogLevels)
|
||||||
|
}
|
||||||
|
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
|
||||||
|
logger = log.With(logger, "caller", log.DefaultCaller)
|
||||||
|
peerName := args[0]
|
||||||
|
|
||||||
|
for i := range allowedIPs {
|
||||||
|
_, aip, err := net.ParseCIDR(allowedIPs[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
connectOpts.allowedIPs = append(connectOpts.allowedIPs, *aip)
|
||||||
|
}
|
||||||
|
|
||||||
|
var privateKey wgtypes.Key
|
||||||
|
var err error
|
||||||
|
if connectOpts.privateKey == "" {
|
||||||
|
privateKey, err = wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to generate private key: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
raw, err := os.ReadFile(connectOpts.privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read private key: %w", err)
|
||||||
|
}
|
||||||
|
privateKey, err = wgtypes.ParseKey(string(raw))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse private key: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publicKey := privateKey.PublicKey()
|
||||||
|
level.Info(logger).Log("msg", "generated public key", "key", publicKey)
|
||||||
|
|
||||||
|
if _, err := opts.kc.KiloV1alpha1().Peers().Get(ctx, peerName, metav1.GetOptions{}); apierrors.IsNotFound(err) {
|
||||||
|
peer := &v1alpha1.Peer{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: peerName,
|
||||||
|
},
|
||||||
|
Spec: v1alpha1.PeerSpec{
|
||||||
|
AllowedIPs: []string{connectOpts.allowedIP.String()},
|
||||||
|
PersistentKeepalive: connectOpts.persistentKeepalive,
|
||||||
|
PublicKey: publicKey.String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if _, err := opts.kc.KiloV1alpha1().Peers().Create(ctx, peer, metav1.CreateOptions{}); err != nil {
|
||||||
|
return fmt.Errorf("failed to create peer: %w", err)
|
||||||
|
}
|
||||||
|
level.Info(logger).Log("msg", "created peer", "peer", peerName)
|
||||||
|
if connectOpts.cleanUp {
|
||||||
|
defer func() {
|
||||||
|
ctxWithTimeout, cancelWithTimeout := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancelWithTimeout()
|
||||||
|
if err := opts.kc.KiloV1alpha1().Peers().Delete(ctxWithTimeout, peerName, metav1.DeleteOptions{}); err != nil {
|
||||||
|
level.Error(logger).Log("err", fmt.Sprintf("failed to delete peer: %v", err))
|
||||||
|
} else {
|
||||||
|
level.Info(logger).Log("msg", "deleted peer", "peer", peerName)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("failed to get peer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, _, err := wireguard.New(connectOpts.interfaceName, connectOpts.mtu)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create wg interface: %w", err)
|
||||||
|
}
|
||||||
|
level.Info(logger).Log("msg", "created WireGuard interface", "name", connectOpts.interfaceName, "index", iface)
|
||||||
|
|
||||||
|
table := route.NewTable()
|
||||||
|
if connectOpts.cleanUp {
|
||||||
|
defer cleanUp(iface, table, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := iproute.SetAddress(iface, &connectOpts.allowedIP); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
level.Info(logger).Log("msg", "set IP address of WireGuard interface", "IP", connectOpts.allowedIP.String())
|
||||||
|
|
||||||
|
if err := iproute.Set(iface, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var g run.Group
|
||||||
|
g.Add(run.SignalHandler(ctx, syscall.SIGINT, syscall.SIGTERM))
|
||||||
|
|
||||||
|
{
|
||||||
|
g.Add(
|
||||||
|
func() error {
|
||||||
|
errCh, err := table.Run(ctx.Done())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to watch for route table updates: %w", err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case err, ok := <-errCh:
|
||||||
|
if ok {
|
||||||
|
level.Error(logger).Log("err", err.Error())
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
cancel()
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
level.Debug(logger).Log("msg", "received signal", "signal", serr.Signal.String(), "err", err.Error())
|
||||||
|
} else {
|
||||||
|
level.Error(logger).Log("msg", "received error", "err", err.Error())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
g.Add(
|
||||||
|
func() error {
|
||||||
|
level.Info(logger).Log("msg", "starting syncer")
|
||||||
|
for {
|
||||||
|
if err := sync(table, peerName, privateKey, iface, logger); err != nil {
|
||||||
|
level.Error(logger).Log("msg", "failed to sync", "err", err.Error())
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(connectOpts.resyncPeriod):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, func(err error) {
|
||||||
|
cancel()
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
level.Debug(logger).Log("msg", "received signal", "signal", serr.Signal.String(), "err", err.Error())
|
||||||
|
} else {
|
||||||
|
level.Error(logger).Log("msg", "received error", "err", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.Run()
|
||||||
|
var serr run.SignalError
|
||||||
|
if ok := errors.As(err, &serr); ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanUp(iface int, t *route.Table, logger log.Logger) {
|
||||||
|
if err := iproute.Set(iface, false); err != nil {
|
||||||
|
level.Error(logger).Log("err", fmt.Sprintf("failed to set WireGuard interface down: %v", err))
|
||||||
|
}
|
||||||
|
if err := iproute.RemoveInterface(iface); err != nil {
|
||||||
|
level.Error(logger).Log("err", fmt.Sprintf("failed to remove WireGuard interface: %v", err))
|
||||||
|
}
|
||||||
|
if err := t.CleanUp(); err != nil {
|
||||||
|
level.Error(logger).Log("failed to clean up routes: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sync(table *route.Table, peerName string, privateKey wgtypes.Key, iface int, logger log.Logger) error {
|
||||||
|
ns, err := opts.backend.Nodes().List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list nodes: %w", err)
|
||||||
|
}
|
||||||
|
for _, n := range ns {
|
||||||
|
_, err := n.Endpoint.UDPAddr(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps, err := opts.backend.Peers().List()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list peers: %w", err)
|
||||||
|
}
|
||||||
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
|
if opts.granularity, err = determineGranularity(opts.granularity, ns); err != nil {
|
||||||
|
return fmt.Errorf("failed to determine granularity: %w", err)
|
||||||
|
}
|
||||||
|
var hostname string
|
||||||
|
var subnet *net.IPNet
|
||||||
|
nodes := make(map[string]*mesh.Node)
|
||||||
|
var nodeNames []string
|
||||||
|
for _, n := range ns {
|
||||||
|
if n.Ready() {
|
||||||
|
nodes[n.Name] = n
|
||||||
|
hostname = n.Name
|
||||||
|
nodeNames = append(nodeNames, n.Name)
|
||||||
|
}
|
||||||
|
if n.WireGuardIP != nil && subnet == nil {
|
||||||
|
subnet = n.WireGuardIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return errors.New("did not find any valid Kilo nodes in the cluster")
|
||||||
|
}
|
||||||
|
if subnet == nil {
|
||||||
|
return errors.New("did not find a valid Kilo subnet on any node")
|
||||||
|
}
|
||||||
|
subnet.IP = subnet.IP.Mask(subnet.Mask)
|
||||||
|
sort.Strings(nodeNames)
|
||||||
|
nodes[nodeNames[0]].AllowedLocationIPs = append(nodes[nodeNames[0]].AllowedLocationIPs, connectOpts.allowedIPs...)
|
||||||
|
peers := make(map[string]*mesh.Peer)
|
||||||
|
for _, p := range ps {
|
||||||
|
if p.Ready() {
|
||||||
|
peers[p.Name] = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := peers[peerName]; !ok {
|
||||||
|
return fmt.Errorf("did not find any peer named %q in the cluster", peerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, opts.port, wgtypes.Key{}, subnet, *peers[peerName].PersistentKeepaliveInterval, logger)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create topology: %w", err)
|
||||||
|
}
|
||||||
|
conf := t.PeerConf(peerName)
|
||||||
|
conf.PrivateKey = &privateKey
|
||||||
|
conf.ListenPort = &opts.port
|
||||||
|
|
||||||
|
wgClient, err := wgctrl.New()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer wgClient.Close()
|
||||||
|
|
||||||
|
current, err := wgClient.Device(connectOpts.interfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var equal bool
|
||||||
|
var diff string
|
||||||
|
equal, diff = conf.Equal(current)
|
||||||
|
if !equal {
|
||||||
|
// If the key is empty, then it's the first time we are running
|
||||||
|
// so don't bother printing a diff.
|
||||||
|
if current.PrivateKey != [wgtypes.KeyLen]byte{} {
|
||||||
|
level.Info(logger).Log("msg", "WireGuard configurations are different", "diff", diff)
|
||||||
|
}
|
||||||
|
level.Debug(logger).Log("msg", "setting WireGuard config", "config", conf.WGConfig())
|
||||||
|
if err := wgClient.ConfigureDevice(connectOpts.interfaceName, conf.WGConfig()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := table.Set(t.PeerRoutes(peerName, iface, connectOpts.allowedIPs)); err != nil {
|
||||||
|
return fmt.Errorf("failed to update route table: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
35
cmd/kgctl/connect_other.go
Normal file
35
cmd/kgctl/connect_other.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2022 the Kilo authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func connect() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "connect",
|
||||||
|
Short: "not supporred on this OS",
|
||||||
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
|
return errors.New("this command is not supported on this OS")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
@@ -18,6 +18,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/mesh"
|
"github.com/squat/kilo/pkg/mesh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,15 +34,15 @@ func graph() *cobra.Command {
|
|||||||
func runGraph(_ *cobra.Command, _ []string) error {
|
func runGraph(_ *cobra.Command, _ []string) error {
|
||||||
ns, err := opts.backend.Nodes().List()
|
ns, err := opts.backend.Nodes().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list nodes: %v", err)
|
return fmt.Errorf("failed to list nodes: %w", err)
|
||||||
}
|
}
|
||||||
ps, err := opts.backend.Peers().List()
|
ps, err := opts.backend.Peers().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %w", err)
|
||||||
}
|
}
|
||||||
// Obtain the Granularity by looking at the annotation of the first node.
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
if opts.granularity, err = determineGranularity(opts.granularity, ns); err != nil {
|
||||||
return fmt.Errorf("failed to obtain granularity: %w", err)
|
return fmt.Errorf("failed to determine granularity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostname string
|
var hostname string
|
||||||
@@ -65,13 +67,13 @@ func runGraph(_ *cobra.Command, _ []string) error {
|
|||||||
peers[p.Name] = p
|
peers[p.Name] = p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, 0, wgtypes.Key{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %w", err)
|
||||||
}
|
}
|
||||||
g, err := t.Dot()
|
g, err := t.Dot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate graph: %v", err)
|
return fmt.Errorf("failed to generate graph: %w", err)
|
||||||
}
|
}
|
||||||
fmt.Println(g)
|
fmt.Println(g)
|
||||||
return nil
|
return nil
|
||||||
|
@@ -21,6 +21,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@@ -61,7 +62,8 @@ var (
|
|||||||
opts struct {
|
opts struct {
|
||||||
backend mesh.Backend
|
backend mesh.Backend
|
||||||
granularity mesh.Granularity
|
granularity mesh.Granularity
|
||||||
port uint32
|
kc kiloclient.Interface
|
||||||
|
port int
|
||||||
}
|
}
|
||||||
backend string
|
backend string
|
||||||
granularity string
|
granularity string
|
||||||
@@ -70,35 +72,39 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func runRoot(_ *cobra.Command, _ []string) error {
|
func runRoot(_ *cobra.Command, _ []string) error {
|
||||||
|
if opts.port < 1 || opts.port > 1<<16-1 {
|
||||||
|
return fmt.Errorf("invalid port: port mus be in range [%d:%d], but got %d", 1, 1<<16-1, opts.port)
|
||||||
|
}
|
||||||
|
|
||||||
opts.granularity = mesh.Granularity(granularity)
|
opts.granularity = mesh.Granularity(granularity)
|
||||||
switch opts.granularity {
|
switch opts.granularity {
|
||||||
case mesh.LogicalGranularity:
|
case mesh.LogicalGranularity:
|
||||||
case mesh.FullGranularity:
|
case mesh.FullGranularity:
|
||||||
case mesh.AutoGranularity:
|
case mesh.AutoGranularity:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("mesh granularity %v unknown; posible values are: %s", granularity, availableGranularities)
|
return fmt.Errorf("mesh granularity %s unknown; posible values are: %s", granularity, availableGranularities)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch backend {
|
switch backend {
|
||||||
case k8s.Backend:
|
case k8s.Backend:
|
||||||
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create Kubernetes config: %v", err)
|
return fmt.Errorf("failed to create Kubernetes config: %w", err)
|
||||||
}
|
}
|
||||||
c := kubernetes.NewForConfigOrDie(config)
|
c := kubernetes.NewForConfigOrDie(config)
|
||||||
kc := kiloclient.NewForConfigOrDie(config)
|
opts.kc = kiloclient.NewForConfigOrDie(config)
|
||||||
ec := apiextensions.NewForConfigOrDie(config)
|
ec := apiextensions.NewForConfigOrDie(config)
|
||||||
opts.backend = k8s.New(c, kc, ec, topologyLabel)
|
opts.backend = k8s.New(c, opts.kc, ec, topologyLabel, log.NewNopLogger())
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("backend %v unknown; posible values are: %s", backend, availableBackends)
|
return fmt.Errorf("backend %s unknown; posible values are: %s", backend, availableBackends)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := opts.backend.Nodes().Init(make(chan struct{})); err != nil {
|
if err := opts.backend.Nodes().Init(make(chan struct{})); err != nil {
|
||||||
return fmt.Errorf("failed to initialize node backend: %v", err)
|
return fmt.Errorf("failed to initialize node backend: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := opts.backend.Peers().Init(make(chan struct{})); err != nil {
|
if err := opts.backend.Peers().Init(make(chan struct{})); err != nil {
|
||||||
return fmt.Errorf("failed to initialize peer backend: %v", err)
|
return fmt.Errorf("failed to initialize peer backend: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -110,6 +116,7 @@ func main() {
|
|||||||
Long: "",
|
Long: "",
|
||||||
PersistentPreRunE: runRoot,
|
PersistentPreRunE: runRoot,
|
||||||
Version: version.Version,
|
Version: version.Version,
|
||||||
|
SilenceErrors: true,
|
||||||
}
|
}
|
||||||
cmd.PersistentFlags().StringVar(&backend, "backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
cmd.PersistentFlags().StringVar(&backend, "backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
||||||
cmd.PersistentFlags().StringVar(&granularity, "mesh-granularity", string(mesh.AutoGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
cmd.PersistentFlags().StringVar(&granularity, "mesh-granularity", string(mesh.AutoGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
||||||
@@ -118,12 +125,13 @@ func main() {
|
|||||||
defaultKubeconfig = filepath.Join(os.Getenv("HOME"), ".kube/config")
|
defaultKubeconfig = filepath.Join(os.Getenv("HOME"), ".kube/config")
|
||||||
}
|
}
|
||||||
cmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", defaultKubeconfig, "Path to kubeconfig.")
|
cmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", defaultKubeconfig, "Path to kubeconfig.")
|
||||||
cmd.PersistentFlags().Uint32Var(&opts.port, "port", mesh.DefaultKiloPort, "The WireGuard port over which the nodes communicate.")
|
cmd.PersistentFlags().IntVar(&opts.port, "port", mesh.DefaultKiloPort, "The WireGuard port over which the nodes communicate.")
|
||||||
cmd.PersistentFlags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
cmd.PersistentFlags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
||||||
|
|
||||||
for _, subCmd := range []*cobra.Command{
|
for _, subCmd := range []*cobra.Command{
|
||||||
graph(),
|
graph(),
|
||||||
showConf(),
|
showConf(),
|
||||||
|
connect(),
|
||||||
} {
|
} {
|
||||||
cmd.AddCommand(subCmd)
|
cmd.AddCommand(subCmd)
|
||||||
}
|
}
|
||||||
@@ -134,7 +142,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func optainGranularity(gr mesh.Granularity, ns []*mesh.Node) (mesh.Granularity, error) {
|
func determineGranularity(gr mesh.Granularity, ns []*mesh.Node) (mesh.Granularity, error) {
|
||||||
if gr == mesh.AutoGranularity {
|
if gr == mesh.AutoGranularity {
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
return gr, errors.New("could not get any nodes")
|
return gr, errors.New("could not get any nodes")
|
||||||
@@ -144,7 +152,7 @@ func optainGranularity(gr mesh.Granularity, ns []*mesh.Node) (mesh.Granularity,
|
|||||||
case mesh.LogicalGranularity:
|
case mesh.LogicalGranularity:
|
||||||
case mesh.FullGranularity:
|
case mesh.FullGranularity:
|
||||||
default:
|
default:
|
||||||
return ret, fmt.Errorf("mesh granularity %v is not supported", opts.granularity)
|
return ret, fmt.Errorf("mesh granularity %s is not supported", opts.granularity)
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -15,14 +15,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@@ -47,7 +48,7 @@ var (
|
|||||||
}, ", ")
|
}, ", ")
|
||||||
allowedIPs []string
|
allowedIPs []string
|
||||||
showConfOpts struct {
|
showConfOpts struct {
|
||||||
allowedIPs []*net.IPNet
|
allowedIPs []net.IPNet
|
||||||
serializer *json.Serializer
|
serializer *json.Serializer
|
||||||
output string
|
output string
|
||||||
asPeer bool
|
asPeer bool
|
||||||
@@ -82,14 +83,14 @@ func runShowConf(c *cobra.Command, args []string) error {
|
|||||||
case outputFormatYAML:
|
case outputFormatYAML:
|
||||||
showConfOpts.serializer = json.NewYAMLSerializer(json.DefaultMetaFactory, peerCreatorTyper{}, peerCreatorTyper{})
|
showConfOpts.serializer = json.NewYAMLSerializer(json.DefaultMetaFactory, peerCreatorTyper{}, peerCreatorTyper{})
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("output format %v unknown; posible values are: %s", showConfOpts.output, availableOutputFormats)
|
return fmt.Errorf("output format %s unknown; posible values are: %s", showConfOpts.output, availableOutputFormats)
|
||||||
}
|
}
|
||||||
for i := range allowedIPs {
|
for i := range allowedIPs {
|
||||||
_, aip, err := net.ParseCIDR(allowedIPs[i])
|
_, aip, err := net.ParseCIDR(allowedIPs[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("allowed-ips must contain only valid CIDRs; got %q", allowedIPs[i])
|
return fmt.Errorf("allowed-ips must contain only valid CIDRs; got %q", allowedIPs[i])
|
||||||
}
|
}
|
||||||
showConfOpts.allowedIPs = append(showConfOpts.allowedIPs, aip)
|
showConfOpts.allowedIPs = append(showConfOpts.allowedIPs, *aip)
|
||||||
}
|
}
|
||||||
return runRoot(c, args)
|
return runRoot(c, args)
|
||||||
}
|
}
|
||||||
@@ -115,15 +116,15 @@ func showConfPeer() *cobra.Command {
|
|||||||
func runShowConfNode(_ *cobra.Command, args []string) error {
|
func runShowConfNode(_ *cobra.Command, args []string) error {
|
||||||
ns, err := opts.backend.Nodes().List()
|
ns, err := opts.backend.Nodes().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list nodes: %v", err)
|
return fmt.Errorf("failed to list nodes: %w", err)
|
||||||
}
|
}
|
||||||
ps, err := opts.backend.Peers().List()
|
ps, err := opts.backend.Peers().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %w", err)
|
||||||
}
|
}
|
||||||
// Obtain the Granularity by looking at the annotation of the first node.
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
if opts.granularity, err = determineGranularity(opts.granularity, ns); err != nil {
|
||||||
return fmt.Errorf("failed to obtain granularity: %w", err)
|
return fmt.Errorf("failed to determine granularity: %w", err)
|
||||||
}
|
}
|
||||||
hostname := args[0]
|
hostname := args[0]
|
||||||
subnet := mesh.DefaultKiloSubnet
|
subnet := mesh.DefaultKiloSubnet
|
||||||
@@ -151,14 +152,14 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, opts.port, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, int(opts.port), wgtypes.Key{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var found bool
|
var found bool
|
||||||
for _, p := range t.PeerConf("").Peers {
|
for _, p := range t.PeerConf("").Peers {
|
||||||
if bytes.Equal(p.PublicKey, nodes[hostname].Key) {
|
if p.PublicKey == nodes[hostname].Key {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
if !showConfOpts.asPeer {
|
if !showConfOpts.asPeer {
|
||||||
c, err := t.Conf().Bytes()
|
c, err := t.Conf().Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate configuration: %v", err)
|
return fmt.Errorf("failed to generate configuration: %w", err)
|
||||||
}
|
}
|
||||||
_, err = os.Stdout.Write(c)
|
_, err = os.Stdout.Write(c)
|
||||||
return err
|
return err
|
||||||
@@ -182,6 +183,9 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case outputFormatYAML:
|
case outputFormatYAML:
|
||||||
p := t.AsPeer()
|
p := t.AsPeer()
|
||||||
|
if p == nil {
|
||||||
|
return errors.New("cannot generate config from nil peer")
|
||||||
|
}
|
||||||
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
||||||
p.DeduplicateIPs()
|
p.DeduplicateIPs()
|
||||||
k8sp := translatePeer(p)
|
k8sp := translatePeer(p)
|
||||||
@@ -189,13 +193,16 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
return showConfOpts.serializer.Encode(k8sp, os.Stdout)
|
return showConfOpts.serializer.Encode(k8sp, os.Stdout)
|
||||||
case outputFormatWireGuard:
|
case outputFormatWireGuard:
|
||||||
p := t.AsPeer()
|
p := t.AsPeer()
|
||||||
|
if p == nil {
|
||||||
|
return errors.New("cannot generate config from nil peer")
|
||||||
|
}
|
||||||
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
||||||
p.DeduplicateIPs()
|
p.DeduplicateIPs()
|
||||||
c, err := (&wireguard.Conf{
|
c, err := (&wireguard.Conf{
|
||||||
Peers: []*wireguard.Peer{p},
|
Peers: []wireguard.Peer{*p},
|
||||||
}).Bytes()
|
}).Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate configuration: %v", err)
|
return fmt.Errorf("failed to generate configuration: %w", err)
|
||||||
}
|
}
|
||||||
_, err = os.Stdout.Write(c)
|
_, err = os.Stdout.Write(c)
|
||||||
return err
|
return err
|
||||||
@@ -206,15 +213,15 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
func runShowConfPeer(_ *cobra.Command, args []string) error {
|
func runShowConfPeer(_ *cobra.Command, args []string) error {
|
||||||
ns, err := opts.backend.Nodes().List()
|
ns, err := opts.backend.Nodes().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list nodes: %v", err)
|
return fmt.Errorf("failed to list nodes: %w", err)
|
||||||
}
|
}
|
||||||
ps, err := opts.backend.Peers().List()
|
ps, err := opts.backend.Peers().List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %w", err)
|
||||||
}
|
}
|
||||||
// Obtain the Granularity by looking at the annotation of the first node.
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
if opts.granularity, err = determineGranularity(opts.granularity, ns); err != nil {
|
||||||
return fmt.Errorf("failed to obtain granularity: %w", err)
|
return fmt.Errorf("failed to determine granularity: %w", err)
|
||||||
}
|
}
|
||||||
var hostname string
|
var hostname string
|
||||||
subnet := mesh.DefaultKiloSubnet
|
subnet := mesh.DefaultKiloSubnet
|
||||||
@@ -244,14 +251,18 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("did not find any peer named %q in the cluster", peer)
|
return fmt.Errorf("did not find any peer named %q in the cluster", peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, mesh.DefaultKiloPort, []byte{}, subnet, peers[peer].PersistentKeepalive, nil)
|
pka := time.Duration(0)
|
||||||
|
if p := peers[peer].PersistentKeepaliveInterval; p != nil {
|
||||||
|
pka = *p
|
||||||
|
}
|
||||||
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, mesh.DefaultKiloPort, wgtypes.Key{}, subnet, pka, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %w", err)
|
||||||
}
|
}
|
||||||
if !showConfOpts.asPeer {
|
if !showConfOpts.asPeer {
|
||||||
c, err := t.PeerConf(peer).Bytes()
|
c, err := t.PeerConf(peer).Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate configuration: %v", err)
|
return fmt.Errorf("failed to generate configuration: %w", err)
|
||||||
}
|
}
|
||||||
_, err = os.Stdout.Write(c)
|
_, err = os.Stdout.Write(c)
|
||||||
return err
|
return err
|
||||||
@@ -272,10 +283,10 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
|
|||||||
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
p.AllowedIPs = append(p.AllowedIPs, showConfOpts.allowedIPs...)
|
||||||
p.DeduplicateIPs()
|
p.DeduplicateIPs()
|
||||||
c, err := (&wireguard.Conf{
|
c, err := (&wireguard.Conf{
|
||||||
Peers: []*wireguard.Peer{p},
|
Peers: []wireguard.Peer{*p},
|
||||||
}).Bytes()
|
}).Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to generate configuration: %v", err)
|
return fmt.Errorf("failed to generate configuration: %w", err)
|
||||||
}
|
}
|
||||||
_, err = os.Stdout.Write(c)
|
_, err = os.Stdout.Write(c)
|
||||||
return err
|
return err
|
||||||
@@ -284,6 +295,7 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// translatePeer translates a wireguard.Peer to a Peer CRD.
|
// translatePeer translates a wireguard.Peer to a Peer CRD.
|
||||||
|
// TODO this function has many similarities to peerBackend.Set(name, peer)
|
||||||
func translatePeer(peer *wireguard.Peer) *v1alpha1.Peer {
|
func translatePeer(peer *wireguard.Peer) *v1alpha1.Peer {
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
return &v1alpha1.Peer{}
|
return &v1alpha1.Peer{}
|
||||||
@@ -291,36 +303,33 @@ func translatePeer(peer *wireguard.Peer) *v1alpha1.Peer {
|
|||||||
var aips []string
|
var aips []string
|
||||||
for _, aip := range peer.AllowedIPs {
|
for _, aip := range peer.AllowedIPs {
|
||||||
// Skip any invalid IPs.
|
// Skip any invalid IPs.
|
||||||
if aip == nil {
|
// TODO all IPs should be valid, so no need to skip here?
|
||||||
|
if aip.String() == (&net.IPNet{}).String() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
aips = append(aips, aip.String())
|
aips = append(aips, aip.String())
|
||||||
}
|
}
|
||||||
var endpoint *v1alpha1.PeerEndpoint
|
var endpoint *v1alpha1.PeerEndpoint
|
||||||
if peer.Endpoint != nil && peer.Endpoint.Port > 0 && (peer.Endpoint.IP != nil || peer.Endpoint.DNS != "") {
|
if peer.Endpoint.Port() > 0 || !peer.Endpoint.HasDNS() {
|
||||||
var ip string
|
|
||||||
if peer.Endpoint.IP != nil {
|
|
||||||
ip = peer.Endpoint.IP.String()
|
|
||||||
}
|
|
||||||
endpoint = &v1alpha1.PeerEndpoint{
|
endpoint = &v1alpha1.PeerEndpoint{
|
||||||
DNSOrIP: v1alpha1.DNSOrIP{
|
DNSOrIP: v1alpha1.DNSOrIP{
|
||||||
DNS: peer.Endpoint.DNS,
|
IP: peer.Endpoint.IP().String(),
|
||||||
IP: ip,
|
DNS: peer.Endpoint.DNS(),
|
||||||
},
|
},
|
||||||
Port: peer.Endpoint.Port,
|
Port: uint32(peer.Endpoint.Port()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var key string
|
var key string
|
||||||
if len(peer.PublicKey) > 0 {
|
if peer.PublicKey != (wgtypes.Key{}) {
|
||||||
key = string(peer.PublicKey)
|
key = peer.PublicKey.String()
|
||||||
}
|
}
|
||||||
var psk string
|
var psk string
|
||||||
if len(peer.PresharedKey) > 0 {
|
if peer.PresharedKey != nil {
|
||||||
psk = string(peer.PresharedKey)
|
psk = peer.PresharedKey.String()
|
||||||
}
|
}
|
||||||
var pka int
|
var pka int
|
||||||
if peer.PersistentKeepalive > 0 {
|
if peer.PersistentKeepaliveInterval != nil && *peer.PersistentKeepaliveInterval > time.Duration(0) {
|
||||||
pka = peer.PersistentKeepalive
|
pka = int(*peer.PersistentKeepaliveInterval)
|
||||||
}
|
}
|
||||||
return &v1alpha1.Peer{
|
return &v1alpha1.Peer{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
962
docs/grafana/kilo.json
Normal file
962
docs/grafana/kilo.json
Normal file
@@ -0,0 +1,962 @@
|
|||||||
|
{
|
||||||
|
"__inputs": [
|
||||||
|
{
|
||||||
|
"name": "DS_PROMETHEUS",
|
||||||
|
"label": "prometheus",
|
||||||
|
"description": "",
|
||||||
|
"type": "datasource",
|
||||||
|
"pluginId": "prometheus",
|
||||||
|
"pluginName": "Prometheus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"__requires": [
|
||||||
|
{
|
||||||
|
"type": "grafana",
|
||||||
|
"id": "grafana",
|
||||||
|
"name": "Grafana",
|
||||||
|
"version": "7.5.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"id": "graph",
|
||||||
|
"name": "Graph",
|
||||||
|
"version": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "datasource",
|
||||||
|
"id": "prometheus",
|
||||||
|
"name": "Prometheus",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"id": "stat",
|
||||||
|
"name": "Stat",
|
||||||
|
"version": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": null,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "Bps"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 12,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "sum by (pod) (rate(wireguard_received_bytes_total[1h])) + sum by (pod) (rate(wireguard_sent_bytes_total[1h]))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:64",
|
||||||
|
"colorMode": "background6",
|
||||||
|
"fill": true,
|
||||||
|
"fillColor": "rgba(234, 112, 112, 0.12)",
|
||||||
|
"line": false,
|
||||||
|
"lineColor": "rgba(237, 46, 24, 0.60)",
|
||||||
|
"op": "time"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Throughput",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:42",
|
||||||
|
"format": "Bps",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:43",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 10,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "(sum(rate(wireguard_sent_bytes_total[5m])) - sum(rate(wireguard_received_bytes_total[5m])))/(sum(rate(wireguard_sent_bytes_total[5m])) + sum(rate(wireguard_received_bytes_total[5m])))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Slip (send - received)",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:502",
|
||||||
|
"format": "percentunit",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:503",
|
||||||
|
"format": "Bps",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 16,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "sum by (public_key) (time() - (wireguard_latest_handshake_seconds!=0))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "latest handshake",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:219",
|
||||||
|
"format": "s",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": "1000",
|
||||||
|
"min": "0",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:220",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 18,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "sum by (instance) (rate(kilo_reconciles_total[30m]))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "kilo reconciles",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:539",
|
||||||
|
"decimals": null,
|
||||||
|
"format": "hertz",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:540",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 4,
|
||||||
|
"x": 0,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"justifyMode": "auto",
|
||||||
|
"orientation": "auto",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"text": {},
|
||||||
|
"textMode": "auto"
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "avg(kilo_peers)",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Kilo Peers",
|
||||||
|
"type": "stat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 4,
|
||||||
|
"x": 4,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"justifyMode": "auto",
|
||||||
|
"orientation": "auto",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"text": {},
|
||||||
|
"textMode": "auto"
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "avg(kilo_nodes)",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Kilo Nodes",
|
||||||
|
"type": "stat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 4,
|
||||||
|
"x": 8,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"id": 8,
|
||||||
|
"options": {
|
||||||
|
"colorMode": "value",
|
||||||
|
"graphMode": "area",
|
||||||
|
"justifyMode": "auto",
|
||||||
|
"orientation": "auto",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"text": {},
|
||||||
|
"textMode": "auto"
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "sum(kilo_leader)",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "segments",
|
||||||
|
"type": "stat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 16
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 6,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "sum by (instance) (rate(kilo_errors_total[10m]))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Kilo Errors",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:446",
|
||||||
|
"format": "hertz",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:447",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 24
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 20,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "sum by (instance) (rate(process_cpu_seconds_total{pod=~\"kilo-.*\"}[1m]))",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "CPU usage",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:162",
|
||||||
|
"format": "percentunit",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:163",
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "${DS_PROMETHEUS}",
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 24
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 22,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.4",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "sum by (instance) (process_resident_memory_bytes{pod=~\"kilo-.*\"})",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"queryType": "randomWalk",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Memory Allocation",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:231",
|
||||||
|
"format": "decbytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:232",
|
||||||
|
"format": "decmbytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"datasource": null,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"id": 14,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Row title",
|
||||||
|
"type": "row"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": false,
|
||||||
|
"schemaVersion": 27,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-24h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Kilo",
|
||||||
|
"uid": "R8Lja3H7z",
|
||||||
|
"version": 11
|
||||||
|
}
|
BIN
docs/graphs/kilo.png
Normal file
BIN
docs/graphs/kilo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 543 KiB |
@@ -31,13 +31,70 @@ make
|
|||||||
|
|
||||||
This will produce a `kgctl` binary at `./bin/<your-os>/<your-architecture>/kgctl`.
|
This will produce a `kgctl` binary at `./bin/<your-os>/<your-architecture>/kgctl`.
|
||||||
|
|
||||||
|
|
||||||
|
### Binary Packages
|
||||||
|
|
||||||
|
#### Arch Linux
|
||||||
|
|
||||||
|
Install `kgctl` from the Arch User Repository using an AUR helper like `paru` or `yay`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
paru -S kgctl-bin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arkade
|
||||||
|
|
||||||
|
The [arkade](https://github.com/alexellis/arkade) CLI can be used to install `kgctl` on any OS and architecture:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
arkade get kgctl
|
||||||
|
```
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|Command|Syntax|Description|
|
|Command|Syntax|Description|
|
||||||
|----|----|-------|
|
|----|----|-------|
|
||||||
|
|[connect](#connect)|`kgctl connect <peer-name> [flags]`|Connect the host to the cluster, setting up the required interfaces, routes, and keys.|
|
||||||
|[graph](#graph)|`kgctl graph [flags]`|Produce a graph in GraphViz format representing the topology of the cluster.|
|
|[graph](#graph)|`kgctl graph [flags]`|Produce a graph in GraphViz format representing the topology of the cluster.|
|
||||||
|[showconf](#showconf)|`kgctl showconf ( node \| peer ) NAME [flags]`|Show the WireGuard configuration for a node or peer in the mesh.|
|
|[showconf](#showconf)|`kgctl showconf ( node \| peer ) <name> [flags]`|Show the WireGuard configuration for a node or peer in the mesh.|
|
||||||
|
|
||||||
|
### connect
|
||||||
|
|
||||||
|
The `connect` command configures the local host as a WireGuard Peer of the cluster and applies all of the necessary networking configuration to connect to the cluster.
|
||||||
|
As long as the process is running, it will watch the cluster for changes and automatically manage the configuration for new or updated Peers and Nodes.
|
||||||
|
If the given Peer name does not exist in the cluster, the command will register a new Peer and generate the necessary WireGuard keys.
|
||||||
|
When the command exits, all of the configuration, including newly registered Peers, is cleaned up.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
PEER_NAME=laptop
|
||||||
|
SERVICECIDR=10.43.0.0/16
|
||||||
|
kgctl connect $PEER_NAME --allowed-ips $SERVICECIDR
|
||||||
|
```
|
||||||
|
|
||||||
|
The local host is now connected to the cluster and all IPs from the cluster and any registered Peers are fully routable.
|
||||||
|
When combined with the `--clean-up false` flag, the configuration produced by the command is persistent and will remain in effect even after the process is stopped.
|
||||||
|
|
||||||
|
With the service CIDR of the cluster routable from the local host, Kubernetes DNS names can now be resolved by the cluster DNS provider.
|
||||||
|
For example, the following snippet could be used to resolve the clusterIP of the Kubernetes API:
|
||||||
|
```shell
|
||||||
|
dig @$(kubectl get service -n kube-system kube-dns -o=jsonpath='{.spec.clusterIP}') kubernetes.default.svc.cluster.local +short
|
||||||
|
# > 10.43.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
For convenience, the cluster DNS provider's IP address can be configured as the local host's DNS server, making Kubernetes DNS names easily resolvable.
|
||||||
|
For example, if using `systemd-resolved`, the following snippet could be used:
|
||||||
|
```shell
|
||||||
|
systemd-resolve --interface kilo0 --set-dns $(kubectl get service -n kube-system kube-dns -o=jsonpath='{.spec.clusterIP}') --set-domain cluster.local
|
||||||
|
# Now all lookups for DNS names ending in `.cluster.local` will be routed over the `kilo0` interface to the cluster DNS provider.
|
||||||
|
dig kubernetes.default.svc.cluster.local +short
|
||||||
|
# > 10.43.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: The `connect` command is currently only supported on Linux.
|
||||||
|
|
||||||
|
> **Note**: The `connect` command requires the `CAP_NET_ADMIN` capability in order to configure the host's networking stack; unprivileged users will need to use `sudo` or similar tools.
|
||||||
|
|
||||||
### graph
|
### graph
|
||||||
|
|
||||||
|
100
docs/monitoring.md
Normal file
100
docs/monitoring.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Monitoring
|
||||||
|
|
||||||
|
The following assumes that you have applied the [kube-prometheus](https://github.com/prometheus-operator/kube-prometheus) monitoring stack onto your cluster.
|
||||||
|
|
||||||
|
## Kilo
|
||||||
|
|
||||||
|
Monitor the Kilo DaemonSet with:
|
||||||
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/podmonitor.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## WireGuard
|
||||||
|
|
||||||
|
Monitor the WireGuard interfaces with:
|
||||||
|
```shell
|
||||||
|
kubectl create ns kilo
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/wg-exporter.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
The manifest will deploy the [Prometheus WireGuard Exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter) as a DaemonSet and a [PodMonitor](https://docs.openshift.com/container-platform/4.8/rest_api/monitoring_apis/podmonitor-monitoring-coreos-com-v1.html).
|
||||||
|
|
||||||
|
By default the kube-prometheus stack only monitors the `default`, `kube-system` and `monitoring` namespaces.
|
||||||
|
In order to allow Prometheus to monitor the `kilo` namespace, apply the Role and RoleBinding with:
|
||||||
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/wg-exporter-role-kube-prometheus.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
### Kilo
|
||||||
|
|
||||||
|
Kilo exports some standard metrics with the Prometheus GoCollector and ProcessCollector.
|
||||||
|
It also exposes some Kilo-specific metrics.
|
||||||
|
|
||||||
|
```
|
||||||
|
# HELP kilo_errors_total Number of errors that occurred while administering the mesh.
|
||||||
|
# TYPE kilo_errors_total counter
|
||||||
|
|
||||||
|
# HELP kilo_leader Leadership status of the node.
|
||||||
|
# TYPE kilo_leader gauge
|
||||||
|
|
||||||
|
# HELP kilo_nodes Number of nodes in the mesh.
|
||||||
|
# TYPE kilo_nodes gauge
|
||||||
|
|
||||||
|
# HELP kilo_peers Number of peers in the mesh.
|
||||||
|
# TYPE kilo_peers gauge
|
||||||
|
|
||||||
|
# HELP kilo_reconciles_total Number of reconciliation attempts.
|
||||||
|
# TYPE kilo_reconciles_total counter
|
||||||
|
```
|
||||||
|
|
||||||
|
### WireGuard
|
||||||
|
|
||||||
|
The [Prometheus WireGuard Exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter) exports the following metrics:
|
||||||
|
|
||||||
|
```
|
||||||
|
# HELP wireguard_sent_bytes_total Bytes sent to the peer
|
||||||
|
# TYPE wireguard_sent_bytes_total counter
|
||||||
|
|
||||||
|
# HELP wireguard_received_bytes_total Bytes received from the peer
|
||||||
|
# TYPE wireguard_received_bytes_total counter
|
||||||
|
|
||||||
|
# HELP wireguard_latest_handshake_seconds Seconds from the last handshake
|
||||||
|
# TYPE wireguard_latest_handshake_seconds gauge
|
||||||
|
```
|
||||||
|
|
||||||
|
## Display some Metrics
|
||||||
|
|
||||||
|
If your laptop is a Kilo peer of the cluster you can access the Prometheus UI by navigating your browser directly to the cluster IP of the `prometheus-k8s` service.
|
||||||
|
Otherwise use `port-forward`:
|
||||||
|
```shell
|
||||||
|
kubectl -n monitoring port-forward svc/prometheus-k8s 9090
|
||||||
|
```
|
||||||
|
and navigate your browser to `localhost:9090`.
|
||||||
|
Check if you can see the PodMonitors for Kilo and the WireGuard Exporter under **Status** -> **Targets** in the Prometheus web UI.
|
||||||
|
|
||||||
|
If you don't see them, check the logs of the `prometheus-k8s` Pods; it may be that Prometheus doesn't have the permission to get Pods in the `kilo` namespace.
|
||||||
|
In this case, you need to apply the Role and RoleBinding from above.
|
||||||
|
|
||||||
|
Navigate to **Graph** and try to execute a simple query, e.g. type `kilo_nodes` and click on `execute`.
|
||||||
|
You should see some data.
|
||||||
|
|
||||||
|
## Using Grafana
|
||||||
|
|
||||||
|
Let's navigate to the Grafana dashboard.
|
||||||
|
Again, if your laptop is not a Kilo peer, use `port-forward`:
|
||||||
|
```shell
|
||||||
|
kubectl -n monitoring port-forward svc/grafana 3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Now navigate your browser to `localhost:3000`.
|
||||||
|
The default user and password is `admin` `admin`.
|
||||||
|
|
||||||
|
An example configuration for a dashboard displaying Kilo metrics can be found [here](https://raw.githubusercontent.com/squat/kilo/main/docs/grafana/kilo.json).
|
||||||
|
You can import this dashboard by hitting **+** -> **Import** on the Grafana dashboard.
|
||||||
|
|
||||||
|
The dashboard looks like this:
|
||||||
|
|
||||||
|
<img src="./graphs/kilo.png" />
|
||||||
|
|
@@ -10,7 +10,7 @@ Support for [Kubernetes network policies](https://kubernetes.io/docs/concepts/se
|
|||||||
The following command adds network policy support by deploying kube-router to work alongside Kilo:
|
The following command adds network policy support by deploying kube-router to work alongside Kilo:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kube-router.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kube-router.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
@@ -9,29 +9,14 @@ Once such a configuration is applied, the Kubernetes API server will send an Adm
|
|||||||
With regard to the [failure policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy), the API server will apply the requested changes to a resource if the request was answered with `"allowed": true`, or deny the changes if the answer was `"allowed": false`.
|
With regard to the [failure policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy), the API server will apply the requested changes to a resource if the request was answered with `"allowed": true`, or deny the changes if the answer was `"allowed": false`.
|
||||||
|
|
||||||
In case of Kilo Peer Validation, the specified operations are `UPDATE` and `CREATE`, the resources are `Peers`, and the default `failurePolicy` is set to `Fail`.
|
In case of Kilo Peer Validation, the specified operations are `UPDATE` and `CREATE`, the resources are `Peers`, and the default `failurePolicy` is set to `Fail`.
|
||||||
View the full ValidatingWebhookConfiguration [here](https://github.com/leonnicolas/kilo-peer-validation/blob/main/deployment-no-cabundle.yaml).
|
View the full ValidatingWebhookConfiguration [here](https://github.com/squat/kilo/blob/main/manifests/peer-validation.yaml).
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
[Kilo-Peer-Validation](https://github.com/leonnicolas/kilo-peer-validation) is a webserver that rejects any AdmissionReviewRequest with a faulty Peer configuration.
|
|
||||||
|
|
||||||
Apply the Service, the Deployment of the actual webserver, and the ValidatingWebhookConfiguration with:
|
Apply the Service, the Deployment of the actual webserver, and the ValidatingWebhookConfiguration with:
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f https://raw.githubusercontent.com/leonnicolas/kilo-peer-validation/main/deployment-no-cabundle.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/blob/main/manifests/peer-validation.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
The Kubernetes API server will only talk to webhook servers via TLS so the Kilo-Peer-Validation server must be given a valid TLS certificate and key, and the API server must be told what certificate authority (CA) to trust.
|
The Kubernetes API server will only talk to webhook servers via TLS so the Kilo-Peer-Validation server must be given a valid TLS certificate and key, and the API server must be told what certificate authority (CA) to trust.
|
||||||
One way to do this is to use the [kube-webhook-certgen](https://github.com/jet/kube-webhook-certgen) project to create a Kubernetes Secret holding the TLS certificate and key for the webhook server and to make a certificate signing request to the Kubernetes API server.
|
The above manifest will use [kube-webhook-certgen](https://github.com/jet/kube-webhook-certgen) to generate the requiered certificates and patch the [ValidatingWebhookConfiguration](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly).
|
||||||
The following snippet can be used to run kube-webhook-certgen in a Docker container to create a Secret and certificate signing request:
|
|
||||||
```shell
|
|
||||||
docker run -v /path/to/kubeconfig:/kubeconfig.yaml:ro jettech/kube-webhook-certgen:v1.5.2 --kubeconfig /kubeconfig.yaml create --namespace kilo --secret-name peer-validation-webhook-tls --host peer-validation,peer-validation.kilo.svc --key-name tls.key --cert-name tls.config
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, the Kubernetes API server can be told what CA to trust by patching the ValidatingWebhookConfiguration with the newly created CA bundle:
|
|
||||||
```shell
|
|
||||||
docker run -v /path/to/kubeconfig:/kubeconfig.yaml:ro jettech/kube-webhook-certgen:v1.5.2 --kubeconfig /kubeconfig.yaml patch --webhook-name peer-validation.kilo.svc --secret-name peer-validation-webhook-tls --namespace kilo --patch-mutating=false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Alternative Method
|
|
||||||
|
|
||||||
An alternative method to generate a ValidatingWebhookConfiguration manifest without using Kubernetes' Certificate Signing API is described in [Kilo-Peer-Validation](https://github.com/leonnicolas/kilo-peer-validation#use-the-set-up-script).
|
|
||||||
|
@@ -18,7 +18,7 @@ test_full_mesh_connectivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_full_mesh_peer() {
|
test_full_mesh_peer() {
|
||||||
check_peer wg1 e2e 10.5.0.1/32 full
|
check_peer wg99 e2e 10.5.0.1/32 full
|
||||||
}
|
}
|
||||||
|
|
||||||
test_full_mesh_allowed_location_ips() {
|
test_full_mesh_allowed_location_ips() {
|
||||||
|
17
e2e/kgctl.sh
Normal file
17
e2e/kgctl.sh
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
block_until_ready_by_name kube-system kilo-userspace
|
||||||
|
_kubectl wait pod -l app.kubernetes.io/name=adjacency --for=condition=Ready --timeout 3m
|
||||||
|
}
|
||||||
|
|
||||||
|
test_connect() {
|
||||||
|
local PEER=test
|
||||||
|
local ALLOWED_IP=10.5.0.1/32
|
||||||
|
docker run -d --name="$PEER" --rm --network=host --cap-add=NET_ADMIN -v "$KGCTL_BINARY":/kgctl -v "$PWD/$KUBECONFIG":/kubeconfig --entrypoint=/kgctl alpine --kubeconfig /kubeconfig connect "$PEER" --allowed-ip "$ALLOWED_IP"
|
||||||
|
assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
|
||||||
|
docker stop "$PEER"
|
||||||
|
}
|
16
e2e/lib.sh
16
e2e/lib.sh
@@ -135,7 +135,7 @@ create_cluster() {
|
|||||||
block_until_ready_by_name default curl
|
block_until_ready_by_name default curl
|
||||||
_kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/master:NoSchedule-
|
_kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/master:NoSchedule-
|
||||||
_kubectl apply -f https://raw.githubusercontent.com/kilo-io/adjacency/main/example.yaml
|
_kubectl apply -f https://raw.githubusercontent.com/kilo-io/adjacency/main/example.yaml
|
||||||
block_until_ready_by_name adjacency adjacency
|
block_until_ready_by_name default adjacency
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_cluster () {
|
delete_cluster () {
|
||||||
@@ -184,14 +184,14 @@ check_peer() {
|
|||||||
local ALLOWED_IP=$3
|
local ALLOWED_IP=$3
|
||||||
local GRANULARITY=$4
|
local GRANULARITY=$4
|
||||||
create_interface "$INTERFACE"
|
create_interface "$INTERFACE"
|
||||||
docker run --rm --entrypoint=/usr/bin/wg "$KILO_IMAGE" genkey > "$INTERFACE"
|
docker run --rm leonnicolas/wg-tools wg genkey > "$INTERFACE"
|
||||||
assert "create_peer $PEER $ALLOWED_IP 10 $(docker run --rm --entrypoint=/bin/sh -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" -c 'cat /key | wg pubkey')" "should be able to create Peer"
|
assert "create_peer $PEER $ALLOWED_IP 10 $(docker run --rm --entrypoint=/bin/sh -v "$PWD/$INTERFACE":/key leonnicolas/wg-tools -c 'cat /key | wg pubkey')" "should be able to create Peer"
|
||||||
assert "_kgctl showconf peer $PEER --mesh-granularity=$GRANULARITY > $PEER.ini" "should be able to get Peer configuration"
|
assert "_kgctl showconf peer $PEER --mesh-granularity=$GRANULARITY > $PEER.ini" "should be able to get Peer configuration"
|
||||||
assert "docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v $PWD/$PEER.ini:/peer.ini $KILO_IMAGE setconf $INTERFACE /peer.ini" "should be able to apply configuration from kgctl"
|
assert "docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v $PWD/$PEER.ini:/peer.ini leonnicolas/wg-tools setconf $INTERFACE /peer.ini" "should be able to apply configuration from kgctl"
|
||||||
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" set "$INTERFACE" private-key /key
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v "$PWD/$INTERFACE":/key leonnicolas/wg-tools set "$INTERFACE" private-key /key
|
||||||
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" address add "$ALLOWED_IP" dev "$INTERFACE"
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools address add "$ALLOWED_IP" dev "$INTERFACE"
|
||||||
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" link set "$INTERFACE" up
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools link set "$INTERFACE" up
|
||||||
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" route add 10.42/16 dev "$INTERFACE"
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip leonnicolas/wg-tools route add 10.42/16 dev "$INTERFACE"
|
||||||
assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
|
assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
|
||||||
assert_equals "$(_kgctl showconf peer "$PEER")" "$(_kgctl showconf peer "$PEER" --mesh-granularity="$GRANULARITY")" "kgctl should be able to auto detect the mesh granularity"
|
assert_equals "$(_kgctl showconf peer "$PEER")" "$(_kgctl showconf peer "$PEER" --mesh-granularity="$GRANULARITY")" "kgctl should be able to auto detect the mesh granularity"
|
||||||
rm "$INTERFACE" "$PEER".ini
|
rm "$INTERFACE" "$PEER".ini
|
||||||
|
@@ -18,7 +18,7 @@ test_location_mesh_connectivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_location_mesh_peer() {
|
test_location_mesh_peer() {
|
||||||
check_peer wg1 e2e 10.5.0.1/32 location
|
check_peer wg99 e2e 10.5.0.1/32 location
|
||||||
}
|
}
|
||||||
|
|
||||||
test_mesh_granularity_auto_detect() {
|
test_mesh_granularity_auto_detect() {
|
||||||
|
65
go.mod
65
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/squat/kilo
|
module github.com/squat/kilo
|
||||||
|
|
||||||
go 1.15
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
|
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
|
||||||
@@ -11,14 +11,14 @@ require (
|
|||||||
github.com/go-kit/kit v0.9.0
|
github.com/go-kit/kit v0.9.0
|
||||||
github.com/imdario/mergo v0.3.6 // indirect
|
github.com/imdario/mergo v0.3.6 // indirect
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
|
||||||
github.com/oklog/run v1.0.0
|
github.com/oklog/run v1.1.0
|
||||||
github.com/prometheus/client_golang v1.7.1
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/spf13/pflag v1.0.5
|
|
||||||
github.com/vishvananda/netlink v1.0.0
|
github.com/vishvananda/netlink v1.0.0
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211124212657-dd7407c86d22
|
||||||
k8s.io/api v0.21.1
|
k8s.io/api v0.21.1
|
||||||
k8s.io/apiextensions-apiserver v0.21.1
|
k8s.io/apiextensions-apiserver v0.21.1
|
||||||
k8s.io/apimachinery v0.21.1
|
k8s.io/apimachinery v0.21.1
|
||||||
@@ -26,3 +26,58 @@ require (
|
|||||||
k8s.io/code-generator v0.21.1
|
k8s.io/code-generator v0.21.1
|
||||||
sigs.k8s.io/controller-tools v0.6.0
|
sigs.k8s.io/controller-tools v0.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/evanphx/json-patch v4.9.0+incompatible // indirect
|
||||||
|
github.com/fatih/color v1.12.0 // indirect
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||||
|
github.com/go-logr/logr v0.4.0 // indirect
|
||||||
|
github.com/gobuffalo/flect v0.2.2 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
|
github.com/google/gofuzz v1.1.0 // indirect
|
||||||
|
github.com/googleapis/gnostic v0.4.1 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.11 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
|
github.com/mdlayher/genetlink v1.0.0 // indirect
|
||||||
|
github.com/mdlayher/netlink v1.4.1 // indirect
|
||||||
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
|
github.com/prometheus/common v0.26.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||||
|
golang.org/x/mod v0.4.2 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
|
golang.org/x/tools v0.1.2 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211123210315-387f7c461a16 // indirect
|
||||||
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
|
google.golang.org/protobuf v1.26.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 // indirect
|
||||||
|
k8s.io/klog/v2 v2.8.0 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||||
|
)
|
||||||
|
107
go.sum
107
go.sum
@@ -41,6 +41,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
|||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
@@ -64,6 +65,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
|||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
|
github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
|
||||||
@@ -104,6 +106,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
|||||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
@@ -114,9 +117,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
|||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||||
@@ -174,6 +179,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/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.0/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.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-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
@@ -233,22 +239,37 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
|
|||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||||
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190 h1:iycCSDo8EKVueI9sfVBBJmtNn9DnXV/K1YWwEJO+uOs=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||||
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
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/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
@@ -271,7 +292,27 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
|
|||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||||
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||||
|
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||||
|
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||||
|
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||||
|
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||||
|
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||||
|
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||||
|
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||||
|
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||||
|
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||||
|
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||||
|
github.com/mdlayher/netlink v1.4.1 h1:I154BCU+mKlIf7BgcAJB2r7QjveNPty6uNY1g9ChVfI=
|
||||||
|
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
|
||||||
|
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
|
||||||
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=
|
||||||
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
@@ -291,14 +332,15 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -328,8 +370,9 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY
|
|||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||||
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
|
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
|
||||||
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
@@ -338,14 +381,16 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
|
|||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||||
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
|
||||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
@@ -355,6 +400,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
@@ -417,6 +463,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
|
||||||
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-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-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -470,6 +519,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191007182048-72f939374954/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -478,11 +528,23 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/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-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
|
||||||
|
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -496,6 +558,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -506,6 +569,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -518,6 +582,7 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -532,16 +597,32 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
|
||||||
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||||
@@ -607,6 +688,12 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
|
||||||
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211123210315-387f7c461a16 h1:SCBV/ayxt56AuC0R8oN5p8Mmc9Llv34tr9VbNlh1kL0=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.0-20211123210315-387f7c461a16/go.mod h1:TjUWrnD5ATh7bFvmm/ALEJZQ4ivKbETb6pmyj1vUoNI=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211124212657-dd7407c86d22 h1:TnoJ6AWs/q4xGE9smgTi+bJmEDet3nrBqdHSV0d5/zA=
|
||||||
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211124212657-dd7407c86d22/go.mod h1:ELrVb7MHNIb8OV6iZ6TP/ScunqUha+vWsUP+SVBH7lQ=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
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.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.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
|
@@ -12,7 +12,7 @@ spec:
|
|||||||
listKind: PeerList
|
listKind: PeerList
|
||||||
plural: peers
|
plural: peers
|
||||||
singular: peer
|
singular: peer
|
||||||
scope: Namespaced
|
scope: Cluster
|
||||||
versions:
|
versions:
|
||||||
- name: v1alpha1
|
- name: v1alpha1
|
||||||
schema:
|
schema:
|
||||||
|
142
manifests/kilo-kubeadm-flannel-userspace.yaml
Normal file
142
manifests/kilo-kubeadm-flannel-userspace.yaml
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- kilo.squat.ai
|
||||||
|
resources:
|
||||||
|
- peers
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apiextensions.k8s.io
|
||||||
|
resources:
|
||||||
|
- customresourcedefinitions
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: kilo
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
serviceAccountName: kilo
|
||||||
|
hostNetwork: true
|
||||||
|
containers:
|
||||||
|
- name: boringtun
|
||||||
|
image: leonnicolas/boringtun
|
||||||
|
args:
|
||||||
|
- --disable-drop-privileges=true
|
||||||
|
- --foreground
|
||||||
|
- kilo0
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
- name: kilo
|
||||||
|
image: squat/kilo
|
||||||
|
args:
|
||||||
|
- --kubeconfig=/etc/kubernetes/kubeconfig
|
||||||
|
- --hostname=$(NODE_NAME)
|
||||||
|
- --create-interface=false
|
||||||
|
- --interface=kilo0
|
||||||
|
- --cni=false
|
||||||
|
- --compatibility=flannel
|
||||||
|
- --local=false
|
||||||
|
env:
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: cni-conf-dir
|
||||||
|
mountPath: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
mountPath: /var/lib/kilo
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
readOnly: true
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
tolerations:
|
||||||
|
- operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: cni-bin-dir
|
||||||
|
hostPath:
|
||||||
|
path: /opt/cni/bin
|
||||||
|
- name: cni-conf-dir
|
||||||
|
hostPath:
|
||||||
|
path: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kilo
|
||||||
|
- name: kubeconfig
|
||||||
|
configMap:
|
||||||
|
name: kube-proxy
|
||||||
|
items:
|
||||||
|
- key: kubeconfig.conf
|
||||||
|
path: kubeconfig
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
- name: wireguard
|
||||||
|
hostPath:
|
||||||
|
path: /var/run/wireguard
|
207
manifests/kilo-kubeadm-userspace.yaml
Normal file
207
manifests/kilo-kubeadm-userspace.yaml
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
data:
|
||||||
|
cni-conf.json: |
|
||||||
|
{
|
||||||
|
"cniVersion":"0.3.1",
|
||||||
|
"name":"kilo",
|
||||||
|
"plugins":[
|
||||||
|
{
|
||||||
|
"name":"kubernetes",
|
||||||
|
"type":"bridge",
|
||||||
|
"bridge":"kube-bridge",
|
||||||
|
"isDefaultGateway":true,
|
||||||
|
"forceAddress":true,
|
||||||
|
"mtu": 1420,
|
||||||
|
"ipam":{
|
||||||
|
"type":"host-local"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type":"portmap",
|
||||||
|
"snat":true,
|
||||||
|
"capabilities":{
|
||||||
|
"portMappings":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- kilo.squat.ai
|
||||||
|
resources:
|
||||||
|
- peers
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apiextensions.k8s.io
|
||||||
|
resources:
|
||||||
|
- customresourcedefinitions
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: kilo
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
serviceAccountName: kilo
|
||||||
|
hostNetwork: true
|
||||||
|
containers:
|
||||||
|
- name: boringtun
|
||||||
|
image: leonnicolas/boringtun
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- --disable-drop-privileges=true
|
||||||
|
- --foreground
|
||||||
|
- kilo0
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
- name: kilo
|
||||||
|
image: squat/kilo
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- --kubeconfig=/etc/kubernetes/kubeconfig
|
||||||
|
- --hostname=$(NODE_NAME)
|
||||||
|
- --create-interface=false
|
||||||
|
- --interface=kilo0
|
||||||
|
env:
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: cni-conf-dir
|
||||||
|
mountPath: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
mountPath: /var/lib/kilo
|
||||||
|
- name: kubeconfig
|
||||||
|
mountPath: /etc/kubernetes
|
||||||
|
readOnly: true
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
readOnly: true
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
initContainers:
|
||||||
|
- name: install-cni
|
||||||
|
image: squat/kilo
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- set -e -x;
|
||||||
|
cp /opt/cni/bin/* /host/opt/cni/bin/;
|
||||||
|
TMP_CONF="$CNI_CONF_NAME".tmp;
|
||||||
|
echo "$CNI_NETWORK_CONFIG" > $TMP_CONF;
|
||||||
|
rm -f /host/etc/cni/net.d/*;
|
||||||
|
mv $TMP_CONF /host/etc/cni/net.d/$CNI_CONF_NAME
|
||||||
|
env:
|
||||||
|
- name: CNI_CONF_NAME
|
||||||
|
value: 10-kilo.conflist
|
||||||
|
- name: CNI_NETWORK_CONFIG
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: kilo
|
||||||
|
key: cni-conf.json
|
||||||
|
volumeMounts:
|
||||||
|
- name: cni-bin-dir
|
||||||
|
mountPath: /host/opt/cni/bin
|
||||||
|
- name: cni-conf-dir
|
||||||
|
mountPath: /host/etc/cni/net.d
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: cni-bin-dir
|
||||||
|
hostPath:
|
||||||
|
path: /opt/cni/bin
|
||||||
|
- name: cni-conf-dir
|
||||||
|
hostPath:
|
||||||
|
path: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kilo
|
||||||
|
- name: kubeconfig
|
||||||
|
configMap:
|
||||||
|
name: kube-proxy
|
||||||
|
items:
|
||||||
|
- key: kubeconfig.conf
|
||||||
|
path: kubeconfig
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
- name: wireguard
|
||||||
|
hostPath:
|
||||||
|
path: /var/run/wireguard
|
173
manifests/peer-validation.yaml
Normal file
173
manifests/peer-validation.yaml
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: "peers.kilo.squat.ai"
|
||||||
|
webhooks:
|
||||||
|
- name: "peers.kilo.squat.ai"
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["kilo.squat.ai"]
|
||||||
|
apiVersions: ["v1alpha1"]
|
||||||
|
operations: ["CREATE","UPDATE"]
|
||||||
|
resources: ["peers"]
|
||||||
|
scope: "Cluster"
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
namespace: "kilo"
|
||||||
|
name: "peer-validation"
|
||||||
|
path: "/validate"
|
||||||
|
admissionReviewVersions: ["v1"]
|
||||||
|
sideEffects: None
|
||||||
|
timeoutSeconds: 5
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: peer-validation-server
|
||||||
|
namespace: kilo
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: peer-validation-server
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: peer-validation-server
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: peer-validation-server
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
containers:
|
||||||
|
- name: server
|
||||||
|
image: squat/kilo
|
||||||
|
args:
|
||||||
|
- webhook
|
||||||
|
- --cert-file=/run/secrets/tls/tls.crt
|
||||||
|
- --key-file=/run/secrets/tls/tls.key
|
||||||
|
- --listen-metrics=:1107
|
||||||
|
- --listen=:8443
|
||||||
|
ports:
|
||||||
|
- containerPort: 8443
|
||||||
|
name: webhook
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
|
volumeMounts:
|
||||||
|
- name: tls
|
||||||
|
mountPath: /run/secrets/tls
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: tls
|
||||||
|
secret:
|
||||||
|
secretName: peer-validation-webhook-tls
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: peer-validation
|
||||||
|
namespace: kilo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: peer-validation-server
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
targetPort: webhook
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kilo-peer-validation
|
||||||
|
namespace: kilo
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: kilo-peer-validation
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- admissionregistration.k8s.io
|
||||||
|
resources:
|
||||||
|
- validatingwebhookconfigurations
|
||||||
|
resourceNames:
|
||||||
|
- peers.kilo.squat.ai
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kilo-peer-validation
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: kilo-peer-validation
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
namespace: kilo
|
||||||
|
name: kilo-peer-validation
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: kilo-peer-validation
|
||||||
|
namespace: kilo
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- create
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kilo-peer-validation
|
||||||
|
namespace: kilo
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: kilo-peer-validation
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
namespace: kilo
|
||||||
|
name: kilo-peer-validation
|
||||||
|
---
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
name: cert-gen
|
||||||
|
namespace: kilo
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
serviceAccountName: kilo-peer-validation
|
||||||
|
initContainers:
|
||||||
|
- name: create
|
||||||
|
image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0
|
||||||
|
args:
|
||||||
|
- create
|
||||||
|
- --namespace=kilo
|
||||||
|
- --secret-name=peer-validation-webhook-tls
|
||||||
|
- --host=peer-validation,peer-validation.kilo.svc
|
||||||
|
- --key-name=tls.key
|
||||||
|
- --cert-name=tls.crt
|
||||||
|
containers:
|
||||||
|
- name: patch
|
||||||
|
image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.0
|
||||||
|
args:
|
||||||
|
- patch
|
||||||
|
- --webhook-name=peers.kilo.squat.ai
|
||||||
|
- --secret-name=peer-validation-webhook-tls
|
||||||
|
- --namespace=kilo
|
||||||
|
- --patch-mutating=false
|
||||||
|
restartPolicy: OnFailure
|
||||||
|
backoffLimit: 4
|
56
manifests/wg-exporter-role-kube-prometheus.yaml
Normal file
56
manifests/wg-exporter-role-kube-prometheus.yaml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: prometheus
|
||||||
|
app.kubernetes.io/name: prometheus
|
||||||
|
app.kubernetes.io/part-of: kube-prometheus
|
||||||
|
app.kubernetes.io/version: 2.26.0
|
||||||
|
name: prometheus-k8s
|
||||||
|
namespace: kilo
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
- endpoints
|
||||||
|
- pods
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/component: prometheus
|
||||||
|
app.kubernetes.io/name: prometheus
|
||||||
|
app.kubernetes.io/part-of: kube-prometheus
|
||||||
|
app.kubernetes.io/version: 2.26.0
|
||||||
|
name: prometheus-k8s
|
||||||
|
namespace: kilo
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: prometheus-k8s
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: prometheus-k8s
|
||||||
|
namespace: monitoring
|
67
manifests/wg-exporter.yaml
Normal file
67
manifests/wg-exporter.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: PodMonitor
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: wg-exporter
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
name: wg-exporter
|
||||||
|
namespace: kilo
|
||||||
|
spec:
|
||||||
|
namespaceSelector:
|
||||||
|
matchNames:
|
||||||
|
- kilo
|
||||||
|
podMetricsEndpoints:
|
||||||
|
- interval: 15s
|
||||||
|
port: metrics
|
||||||
|
path: /metrics
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
app.kubernetes.io/name: wg-exporter
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: wg-exporter
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
name: wg-exporter
|
||||||
|
namespace: kilo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: wg-exporter
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: wg-exporter
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- -a
|
||||||
|
- -i=kilo0
|
||||||
|
- -p=9586
|
||||||
|
image: mindflavor/prometheus-wireguard-exporter
|
||||||
|
name: wg-exporter
|
||||||
|
ports:
|
||||||
|
- containerPort: 9586
|
||||||
|
name: metrics
|
||||||
|
protocol: TCP
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
terminationMessagePath: /dev/termination-log
|
||||||
|
terminationMessagePolicy: File
|
||||||
|
volumeMounts:
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
volumes:
|
||||||
|
- name: wireguard
|
||||||
|
hostPath:
|
||||||
|
path: /var/run/wireguard
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
@@ -74,7 +74,7 @@ func (i *ipip) Rules(nodes []*net.IPNet) []iptables.Rule {
|
|||||||
rules = append(rules, iptables.NewIPv6Rule("filter", "INPUT", "-p", proto, "-m", "comment", "--comment", "Kilo: jump to IPIP chain", "-j", "KILO-IPIP"))
|
rules = append(rules, iptables.NewIPv6Rule("filter", "INPUT", "-p", proto, "-m", "comment", "--comment", "Kilo: jump to IPIP chain", "-j", "KILO-IPIP"))
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
// Accept encapsulated traffic from peers.
|
// Accept encapsulated traffic from peers.
|
||||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(n.IP)), "filter", "KILO-IPIP", "-s", n.String(), "-m", "comment", "--comment", "Kilo: allow IPIP traffic", "-j", "ACCEPT"))
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(n.IP), "filter", "KILO-IPIP", "-s", n.String(), "-m", "comment", "--comment", "Kilo: allow IPIP traffic", "-j", "ACCEPT"))
|
||||||
}
|
}
|
||||||
// Drop all other IPIP traffic.
|
// Drop all other IPIP traffic.
|
||||||
rules = append(rules, iptables.NewIPv4Rule("filter", "INPUT", "-p", proto, "-m", "comment", "--comment", "Kilo: reject other IPIP traffic", "-j", "DROP"))
|
rules = append(rules, iptables.NewIPv4Rule("filter", "INPUT", "-p", proto, "-m", "comment", "--comment", "Kilo: reject other IPIP traffic", "-j", "DROP"))
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build cgo
|
||||||
// +build cgo
|
// +build cgo
|
||||||
|
|
||||||
package encapsulation
|
package encapsulation
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build !cgo
|
||||||
// +build !cgo
|
// +build !cgo
|
||||||
|
|
||||||
package encapsulation
|
package encapsulation
|
||||||
|
@@ -16,7 +16,9 @@ package iptables
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -25,6 +27,21 @@ import (
|
|||||||
"github.com/go-kit/kit/log/level"
|
"github.com/go-kit/kit/log/level"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ipv6ModuleDisabledPath = "/sys/module/ipv6/parameters/disable"
|
||||||
|
|
||||||
|
func ipv6Disabled() (bool, error) {
|
||||||
|
f, err := os.Open(ipv6ModuleDisabledPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
disabled := make([]byte, 1)
|
||||||
|
if _, err = io.ReadFull(f, disabled); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return disabled[0] == '1', nil
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol represents an IP protocol.
|
// Protocol represents an IP protocol.
|
||||||
type Protocol byte
|
type Protocol byte
|
||||||
|
|
||||||
@@ -36,11 +53,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetProtocol will return a protocol from the length of an IP address.
|
// GetProtocol will return a protocol from the length of an IP address.
|
||||||
func GetProtocol(length int) Protocol {
|
func GetProtocol(ip net.IP) Protocol {
|
||||||
if length == net.IPv6len {
|
if len(ip) == net.IPv4len || ip.To4() != nil {
|
||||||
return ProtocolIPv6
|
|
||||||
}
|
|
||||||
return ProtocolIPv4
|
return ProtocolIPv4
|
||||||
|
}
|
||||||
|
return ProtocolIPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client represents any type that can administer iptables rules.
|
// Client represents any type that can administer iptables rules.
|
||||||
@@ -253,12 +270,21 @@ func New(opts ...ControllerOption) (*Controller, error) {
|
|||||||
c.v4 = v4
|
c.v4 = v4
|
||||||
}
|
}
|
||||||
if c.v6 == nil {
|
if c.v6 == nil {
|
||||||
|
disabled, err := ipv6Disabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to check IPv6 status: %v", err)
|
||||||
|
}
|
||||||
|
if disabled {
|
||||||
|
level.Info(c.logger).Log("msg", "IPv6 is disabled in the kernel; disabling the IPv6 iptables controller")
|
||||||
|
c.v6 = &fakeClient{}
|
||||||
|
} else {
|
||||||
v6, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
v6, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create iptables IPv6 client: %v", err)
|
return nil, fmt.Errorf("failed to create iptables IPv6 client: %v", err)
|
||||||
}
|
}
|
||||||
c.v6 = v6
|
c.v6 = v6
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ var PeerShortNames = []string{"peer"}
|
|||||||
// +genclient:nonNamespaced
|
// +genclient:nonNamespaced
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
// +k8s:openapi-gen=true
|
// +k8s:openapi-gen=true
|
||||||
|
// +kubebuilder:resource:scope=Cluster
|
||||||
|
|
||||||
// Peer is a WireGuard peer that should have access to the VPN.
|
// Peer is a WireGuard peer that should have access to the VPN.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
|
//go:build !ignore_autogenerated
|
||||||
// +build !ignore_autogenerated
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -25,13 +25,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/go-kit/kit/log/level"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
|
||||||
v1informers "k8s.io/client-go/informers/core/v1"
|
v1informers "k8s.io/client-go/informers/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
v1listers "k8s.io/client-go/listers/core/v1"
|
v1listers "k8s.io/client-go/listers/core/v1"
|
||||||
@@ -67,6 +69,8 @@ const (
|
|||||||
jsonRemovePatch = `{"op": "remove", "path": "%s"}`
|
jsonRemovePatch = `{"op": "remove", "path": "%s"}`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger = log.NewNopLogger()
|
||||||
|
|
||||||
type backend struct {
|
type backend struct {
|
||||||
nodes *nodeBackend
|
nodes *nodeBackend
|
||||||
peers *peerBackend
|
peers *peerBackend
|
||||||
@@ -99,10 +103,12 @@ type peerBackend struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new instance of a mesh.Backend.
|
// New creates a new instance of a mesh.Backend.
|
||||||
func New(c kubernetes.Interface, kc kiloclient.Interface, ec apiextensions.Interface, topologyLabel string) mesh.Backend {
|
func New(c kubernetes.Interface, kc kiloclient.Interface, ec apiextensions.Interface, topologyLabel string, l log.Logger) mesh.Backend {
|
||||||
ni := v1informers.NewNodeInformer(c, 5*time.Minute, nil)
|
ni := v1informers.NewNodeInformer(c, 5*time.Minute, nil)
|
||||||
pi := v1alpha1informers.NewPeerInformer(kc, 5*time.Minute, nil)
|
pi := v1alpha1informers.NewPeerInformer(kc, 5*time.Minute, nil)
|
||||||
|
|
||||||
|
logger = l
|
||||||
|
|
||||||
return &backend{
|
return &backend{
|
||||||
&nodeBackend{
|
&nodeBackend{
|
||||||
client: c,
|
client: c,
|
||||||
@@ -218,7 +224,7 @@ func (nb *nodeBackend) Set(name string, node *mesh.Node) error {
|
|||||||
} else {
|
} else {
|
||||||
n.ObjectMeta.Annotations[internalIPAnnotationKey] = node.InternalIP.String()
|
n.ObjectMeta.Annotations[internalIPAnnotationKey] = node.InternalIP.String()
|
||||||
}
|
}
|
||||||
n.ObjectMeta.Annotations[keyAnnotationKey] = string(node.Key)
|
n.ObjectMeta.Annotations[keyAnnotationKey] = node.Key.String()
|
||||||
n.ObjectMeta.Annotations[lastSeenAnnotationKey] = strconv.FormatInt(node.LastSeen, 10)
|
n.ObjectMeta.Annotations[lastSeenAnnotationKey] = strconv.FormatInt(node.LastSeen, 10)
|
||||||
if node.WireGuardIP == nil {
|
if node.WireGuardIP == nil {
|
||||||
n.ObjectMeta.Annotations[wireGuardIPAnnotationKey] = ""
|
n.ObjectMeta.Annotations[wireGuardIPAnnotationKey] = ""
|
||||||
@@ -276,9 +282,9 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
location = node.ObjectMeta.Labels[topologyLabel]
|
location = node.ObjectMeta.Labels[topologyLabel]
|
||||||
}
|
}
|
||||||
// Allow the endpoint to be overridden.
|
// Allow the endpoint to be overridden.
|
||||||
endpoint := parseEndpoint(node.ObjectMeta.Annotations[forceEndpointAnnotationKey])
|
endpoint := wireguard.ParseEndpoint(node.ObjectMeta.Annotations[forceEndpointAnnotationKey])
|
||||||
if endpoint == nil {
|
if endpoint == nil {
|
||||||
endpoint = parseEndpoint(node.ObjectMeta.Annotations[endpointAnnotationKey])
|
endpoint = wireguard.ParseEndpoint(node.ObjectMeta.Annotations[endpointAnnotationKey])
|
||||||
}
|
}
|
||||||
// Allow the internal IP to be overridden.
|
// Allow the internal IP to be overridden.
|
||||||
internalIP := normalizeIP(node.ObjectMeta.Annotations[forceInternalIPAnnotationKey])
|
internalIP := normalizeIP(node.ObjectMeta.Annotations[forceInternalIPAnnotationKey])
|
||||||
@@ -292,13 +298,11 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
internalIP = nil
|
internalIP = nil
|
||||||
}
|
}
|
||||||
// Set Wireguard PersistentKeepalive setting for the node.
|
// Set Wireguard PersistentKeepalive setting for the node.
|
||||||
var persistentKeepalive int64
|
var persistentKeepalive time.Duration
|
||||||
if keepAlive, ok := node.ObjectMeta.Annotations[persistentKeepaliveKey]; !ok {
|
if keepAlive, ok := node.ObjectMeta.Annotations[persistentKeepaliveKey]; ok {
|
||||||
persistentKeepalive = 0
|
// We can ignore the error, because p will be set to 0 if an error occures.
|
||||||
} else {
|
p, _ := strconv.ParseInt(keepAlive, 10, 64)
|
||||||
if persistentKeepalive, err = strconv.ParseInt(keepAlive, 10, 64); err != nil {
|
persistentKeepalive = time.Duration(p) * time.Second
|
||||||
persistentKeepalive = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var lastSeen int64
|
var lastSeen int64
|
||||||
if ls, ok := node.ObjectMeta.Annotations[lastSeenAnnotationKey]; !ok {
|
if ls, ok := node.ObjectMeta.Annotations[lastSeenAnnotationKey]; !ok {
|
||||||
@@ -308,7 +312,7 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
lastSeen = 0
|
lastSeen = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var discoveredEndpoints map[string]*wireguard.Endpoint
|
var discoveredEndpoints map[string]*net.UDPAddr
|
||||||
if de, ok := node.ObjectMeta.Annotations[discoveredEndpointsKey]; ok {
|
if de, ok := node.ObjectMeta.Annotations[discoveredEndpointsKey]; ok {
|
||||||
err := json.Unmarshal([]byte(de), &discoveredEndpoints)
|
err := json.Unmarshal([]byte(de), &discoveredEndpoints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -316,11 +320,11 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set allowed IPs for a location.
|
// Set allowed IPs for a location.
|
||||||
var allowedLocationIPs []*net.IPNet
|
var allowedLocationIPs []net.IPNet
|
||||||
if str, ok := node.ObjectMeta.Annotations[allowedLocationIPsKey]; ok {
|
if str, ok := node.ObjectMeta.Annotations[allowedLocationIPsKey]; ok {
|
||||||
for _, ip := range strings.Split(str, ",") {
|
for _, ip := range strings.Split(str, ",") {
|
||||||
if ipnet := normalizeIP(ip); ipnet != nil {
|
if ipnet := normalizeIP(ip); ipnet != nil {
|
||||||
allowedLocationIPs = append(allowedLocationIPs, ipnet)
|
allowedLocationIPs = append(allowedLocationIPs, *ipnet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,6 +339,9 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO log some error or warning.
|
||||||
|
key, _ := wgtypes.ParseKey(node.ObjectMeta.Annotations[keyAnnotationKey])
|
||||||
|
|
||||||
return &mesh.Node{
|
return &mesh.Node{
|
||||||
// Endpoint and InternalIP should only ever fail to parse if the
|
// Endpoint and InternalIP should only ever fail to parse if the
|
||||||
// remote node's agent has not yet set its IP address;
|
// remote node's agent has not yet set its IP address;
|
||||||
@@ -345,12 +352,12 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
NoInternalIP: noInternalIP,
|
NoInternalIP: noInternalIP,
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte(node.ObjectMeta.Annotations[keyAnnotationKey]),
|
Key: key,
|
||||||
LastSeen: lastSeen,
|
LastSeen: lastSeen,
|
||||||
Leader: leader,
|
Leader: leader,
|
||||||
Location: location,
|
Location: location,
|
||||||
Name: node.Name,
|
Name: node.Name,
|
||||||
PersistentKeepalive: int(persistentKeepalive),
|
PersistentKeepalive: persistentKeepalive,
|
||||||
Subnet: subnet,
|
Subnet: subnet,
|
||||||
// WireGuardIP can fail to parse if the node is not a leader or if
|
// WireGuardIP can fail to parse if the node is not a leader or if
|
||||||
// the node's agent has not yet reconciled. In either case, the IP
|
// the node's agent has not yet reconciled. In either case, the IP
|
||||||
@@ -367,14 +374,14 @@ func translatePeer(peer *v1alpha1.Peer) *mesh.Peer {
|
|||||||
if peer == nil {
|
if peer == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var aips []*net.IPNet
|
var aips []net.IPNet
|
||||||
for _, aip := range peer.Spec.AllowedIPs {
|
for _, aip := range peer.Spec.AllowedIPs {
|
||||||
aip := normalizeIP(aip)
|
aip := normalizeIP(aip)
|
||||||
// Skip any invalid IPs.
|
// Skip any invalid IPs.
|
||||||
if aip == nil {
|
if aip == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
aips = append(aips, aip)
|
aips = append(aips, *aip)
|
||||||
}
|
}
|
||||||
var endpoint *wireguard.Endpoint
|
var endpoint *wireguard.Endpoint
|
||||||
if peer.Spec.Endpoint != nil {
|
if peer.Spec.Endpoint != nil {
|
||||||
@@ -384,37 +391,42 @@ func translatePeer(peer *v1alpha1.Peer) *mesh.Peer {
|
|||||||
} else {
|
} else {
|
||||||
ip = ip.To16()
|
ip = ip.To16()
|
||||||
}
|
}
|
||||||
if peer.Spec.Endpoint.Port > 0 && (ip != nil || peer.Spec.Endpoint.DNS != "") {
|
if peer.Spec.Endpoint.Port > 0 {
|
||||||
endpoint = &wireguard.Endpoint{
|
if ip != nil {
|
||||||
DNSOrIP: wireguard.DNSOrIP{
|
endpoint = wireguard.NewEndpoint(ip, int(peer.Spec.Endpoint.Port))
|
||||||
DNS: peer.Spec.Endpoint.DNS,
|
}
|
||||||
IP: ip,
|
if peer.Spec.Endpoint.DNS != "" {
|
||||||
},
|
endpoint = wireguard.ParseEndpoint(fmt.Sprintf("%s:%d", peer.Spec.Endpoint.DNS, peer.Spec.Endpoint.Port))
|
||||||
Port: peer.Spec.Endpoint.Port,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var key []byte
|
|
||||||
if len(peer.Spec.PublicKey) > 0 {
|
key, err := wgtypes.ParseKey(peer.Spec.PublicKey)
|
||||||
key = []byte(peer.Spec.PublicKey)
|
if err != nil {
|
||||||
|
level.Error(logger).Log("msg", "failed to parse public key", "peer", peer.Name, "err", err.Error())
|
||||||
}
|
}
|
||||||
var psk []byte
|
var psk *wgtypes.Key
|
||||||
if len(peer.Spec.PresharedKey) > 0 {
|
if k, err := wgtypes.ParseKey(peer.Spec.PresharedKey); err != nil {
|
||||||
psk = []byte(peer.Spec.PresharedKey)
|
// Set key to nil to avoid setting a key to the zero value wgtypes.Key{}
|
||||||
|
psk = nil
|
||||||
|
} else {
|
||||||
|
psk = &k
|
||||||
}
|
}
|
||||||
var pka int
|
var pka time.Duration
|
||||||
if peer.Spec.PersistentKeepalive > 0 {
|
if peer.Spec.PersistentKeepalive > 0 {
|
||||||
pka = peer.Spec.PersistentKeepalive
|
pka = time.Duration(peer.Spec.PersistentKeepalive) * time.Second
|
||||||
}
|
}
|
||||||
return &mesh.Peer{
|
return &mesh.Peer{
|
||||||
Name: peer.Name,
|
Name: peer.Name,
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: aips,
|
AllowedIPs: aips,
|
||||||
Endpoint: endpoint,
|
PersistentKeepaliveInterval: &pka,
|
||||||
PersistentKeepalive: pka,
|
|
||||||
PresharedKey: psk,
|
PresharedKey: psk,
|
||||||
PublicKey: key,
|
PublicKey: key,
|
||||||
},
|
},
|
||||||
|
Endpoint: endpoint,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,21 +523,25 @@ func (pb *peerBackend) Set(name string, peer *mesh.Peer) error {
|
|||||||
p.Spec.AllowedIPs[i] = peer.AllowedIPs[i].String()
|
p.Spec.AllowedIPs[i] = peer.AllowedIPs[i].String()
|
||||||
}
|
}
|
||||||
if peer.Endpoint != nil {
|
if peer.Endpoint != nil {
|
||||||
var ip string
|
|
||||||
if peer.Endpoint.IP != nil {
|
|
||||||
ip = peer.Endpoint.IP.String()
|
|
||||||
}
|
|
||||||
p.Spec.Endpoint = &v1alpha1.PeerEndpoint{
|
p.Spec.Endpoint = &v1alpha1.PeerEndpoint{
|
||||||
DNSOrIP: v1alpha1.DNSOrIP{
|
DNSOrIP: v1alpha1.DNSOrIP{
|
||||||
IP: ip,
|
IP: peer.Endpoint.IP().String(),
|
||||||
DNS: peer.Endpoint.DNS,
|
DNS: peer.Endpoint.DNS(),
|
||||||
},
|
},
|
||||||
Port: peer.Endpoint.Port,
|
Port: uint32(peer.Endpoint.Port()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Spec.PersistentKeepalive = peer.PersistentKeepalive
|
if peer.PersistentKeepaliveInterval == nil {
|
||||||
p.Spec.PresharedKey = string(peer.PresharedKey)
|
p.Spec.PersistentKeepalive = 0
|
||||||
p.Spec.PublicKey = string(peer.PublicKey)
|
} else {
|
||||||
|
p.Spec.PersistentKeepalive = int(*peer.PersistentKeepaliveInterval / time.Second)
|
||||||
|
}
|
||||||
|
if peer.PresharedKey == nil {
|
||||||
|
p.Spec.PresharedKey = ""
|
||||||
|
} else {
|
||||||
|
p.Spec.PresharedKey = peer.PresharedKey.String()
|
||||||
|
}
|
||||||
|
p.Spec.PublicKey = peer.PublicKey.String()
|
||||||
if _, err = pb.client.KiloV1alpha1().Peers().Update(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
|
if _, err = pb.client.KiloV1alpha1().Peers().Update(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
|
||||||
return fmt.Errorf("failed to update peer: %v", err)
|
return fmt.Errorf("failed to update peer: %v", err)
|
||||||
}
|
}
|
||||||
@@ -549,35 +565,3 @@ func normalizeIP(ip string) *net.IPNet {
|
|||||||
ipNet.IP = i.To16()
|
ipNet.IP = i.To16()
|
||||||
return ipNet
|
return ipNet
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEndpoint(endpoint string) *wireguard.Endpoint {
|
|
||||||
if len(endpoint) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
parts := strings.Split(endpoint, ":")
|
|
||||||
if len(parts) < 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
portRaw := parts[len(parts)-1]
|
|
||||||
hostRaw := strings.Trim(strings.Join(parts[:len(parts)-1], ":"), "[]")
|
|
||||||
port, err := strconv.ParseUint(portRaw, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(validation.IsValidPortNum(int(port))) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ip := net.ParseIP(hostRaw)
|
|
||||||
if ip == nil {
|
|
||||||
if len(validation.IsDNS1123Subdomain(hostRaw)) == 0 {
|
|
||||||
return &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{DNS: hostRaw}, Port: uint32(port)}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
ip = ip.To16()
|
|
||||||
}
|
|
||||||
return &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: ip}, Port: uint32(port)}
|
|
||||||
}
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -17,8 +17,10 @@ package k8s
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kylelemons/godebug/pretty"
|
"github.com/kylelemons/godebug/pretty"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
"github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
@@ -26,6 +28,30 @@ import (
|
|||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mustKey() (k wgtypes.Key) {
|
||||||
|
var err error
|
||||||
|
if k, err = wgtypes.GeneratePrivateKey(); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustPSKKey() (key *wgtypes.Key) {
|
||||||
|
if k, err := wgtypes.GenerateKey(); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
} else {
|
||||||
|
key = &k
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fooKey = mustKey()
|
||||||
|
pskKey = mustPSKKey()
|
||||||
|
second = time.Second
|
||||||
|
zero = time.Duration(0)
|
||||||
|
)
|
||||||
|
|
||||||
func TestTranslateNode(t *testing.T) {
|
func TestTranslateNode(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -54,8 +80,19 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
internalIPAnnotationKey: "10.0.0.2/32",
|
internalIPAnnotationKey: "10.0.0.2/32",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")}, Port: mesh.DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.1").To4(), mesh.DefaultKiloPort),
|
||||||
InternalIP: &net.IPNet{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(32, 32)},
|
InternalIP: &net.IPNet{IP: net.ParseIP("10.0.0.2").To4(), Mask: net.CIDRMask(32, 32)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid ips with ipv6",
|
||||||
|
annotations: map[string]string{
|
||||||
|
endpointAnnotationKey: "[ff10::10]:51820",
|
||||||
|
internalIPAnnotationKey: "ff60::10/64",
|
||||||
|
},
|
||||||
|
out: &mesh.Node{
|
||||||
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("ff10::10").To16(), mesh.DefaultKiloPort),
|
||||||
|
InternalIP: &net.IPNet{IP: net.ParseIP("ff60::10").To16(), Mask: net.CIDRMask(64, 128)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,7 +105,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
name: "normalize subnet",
|
name: "normalize subnet",
|
||||||
annotations: map[string]string{},
|
annotations: map[string]string{},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0").To4(), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
subnet: "10.2.0.1/24",
|
subnet: "10.2.0.1/24",
|
||||||
},
|
},
|
||||||
@@ -76,7 +113,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
name: "valid subnet",
|
name: "valid subnet",
|
||||||
annotations: map[string]string{},
|
annotations: map[string]string{},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0").To4(), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
subnet: "10.2.1.0/24",
|
subnet: "10.2.1.0/24",
|
||||||
},
|
},
|
||||||
@@ -108,7 +145,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
forceEndpointAnnotationKey: "-10.0.0.2:51821",
|
forceEndpointAnnotationKey: "-10.0.0.2:51821",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")}, Port: mesh.DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.1").To4(), mesh.DefaultKiloPort),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -118,7 +155,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
forceEndpointAnnotationKey: "10.0.0.2:51821",
|
forceEndpointAnnotationKey: "10.0.0.2:51821",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.2")}, Port: 51821},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.2").To4(), 51821),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -127,7 +164,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
persistentKeepaliveKey: "25",
|
persistentKeepaliveKey: "25",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
PersistentKeepalive: 25,
|
PersistentKeepalive: 25 * time.Second,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -137,7 +174,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
forceInternalIPAnnotationKey: "-10.1.0.2/24",
|
forceInternalIPAnnotationKey: "-10.1.0.2/24",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.1"), Mask: net.CIDRMask(24, 32)},
|
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(24, 32)},
|
||||||
NoInternalIP: false,
|
NoInternalIP: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -148,7 +185,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
forceInternalIPAnnotationKey: "10.1.0.2/24",
|
forceInternalIPAnnotationKey: "10.1.0.2/24",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.2"), Mask: net.CIDRMask(24, 32)},
|
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.2").To4(), Mask: net.CIDRMask(24, 32)},
|
||||||
NoInternalIP: false,
|
NoInternalIP: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -166,7 +203,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
forceEndpointAnnotationKey: "10.0.0.2:51821",
|
forceEndpointAnnotationKey: "10.0.0.2:51821",
|
||||||
forceInternalIPAnnotationKey: "10.1.0.2/32",
|
forceInternalIPAnnotationKey: "10.1.0.2/32",
|
||||||
internalIPAnnotationKey: "10.1.0.1/32",
|
internalIPAnnotationKey: "10.1.0.1/32",
|
||||||
keyAnnotationKey: "foo",
|
keyAnnotationKey: fooKey.String(),
|
||||||
lastSeenAnnotationKey: "1000000000",
|
lastSeenAnnotationKey: "1000000000",
|
||||||
leaderAnnotationKey: "",
|
leaderAnnotationKey: "",
|
||||||
locationAnnotationKey: "b",
|
locationAnnotationKey: "b",
|
||||||
@@ -177,14 +214,45 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
RegionLabelKey: "a",
|
RegionLabelKey: "a",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.2")}, Port: 51821},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.2").To4(), 51821),
|
||||||
NoInternalIP: false,
|
NoInternalIP: false,
|
||||||
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.2"), Mask: net.CIDRMask(32, 32)},
|
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.2").To4(), Mask: net.CIDRMask(32, 32)},
|
||||||
Key: []byte("foo"),
|
Key: fooKey,
|
||||||
LastSeen: 1000000000,
|
LastSeen: 1000000000,
|
||||||
Leader: true,
|
Leader: true,
|
||||||
Location: "b",
|
Location: "b",
|
||||||
PersistentKeepalive: 25,
|
PersistentKeepalive: 25 * time.Second,
|
||||||
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0").To4(), Mask: net.CIDRMask(24, 32)},
|
||||||
|
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1").To4(), Mask: net.CIDRMask(16, 32)},
|
||||||
|
},
|
||||||
|
subnet: "10.2.1.0/24",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complete with ipv6",
|
||||||
|
annotations: map[string]string{
|
||||||
|
endpointAnnotationKey: "10.0.0.1:51820",
|
||||||
|
forceEndpointAnnotationKey: "[1100::10]:51821",
|
||||||
|
forceInternalIPAnnotationKey: "10.1.0.2/32",
|
||||||
|
internalIPAnnotationKey: "10.1.0.1/32",
|
||||||
|
keyAnnotationKey: fooKey.String(),
|
||||||
|
lastSeenAnnotationKey: "1000000000",
|
||||||
|
leaderAnnotationKey: "",
|
||||||
|
locationAnnotationKey: "b",
|
||||||
|
persistentKeepaliveKey: "25",
|
||||||
|
wireGuardIPAnnotationKey: "10.4.0.1/16",
|
||||||
|
},
|
||||||
|
labels: map[string]string{
|
||||||
|
RegionLabelKey: "a",
|
||||||
|
},
|
||||||
|
out: &mesh.Node{
|
||||||
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("1100::10"), 51821),
|
||||||
|
NoInternalIP: false,
|
||||||
|
InternalIP: &net.IPNet{IP: net.ParseIP("10.1.0.2"), Mask: net.CIDRMask(32, 32)},
|
||||||
|
Key: fooKey,
|
||||||
|
LastSeen: 1000000000,
|
||||||
|
Leader: true,
|
||||||
|
Location: "b",
|
||||||
|
PersistentKeepalive: 25 * time.Second,
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
@@ -195,7 +263,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
annotations: map[string]string{
|
annotations: map[string]string{
|
||||||
endpointAnnotationKey: "10.0.0.1:51820",
|
endpointAnnotationKey: "10.0.0.1:51820",
|
||||||
internalIPAnnotationKey: "",
|
internalIPAnnotationKey: "",
|
||||||
keyAnnotationKey: "foo",
|
keyAnnotationKey: fooKey.String(),
|
||||||
lastSeenAnnotationKey: "1000000000",
|
lastSeenAnnotationKey: "1000000000",
|
||||||
locationAnnotationKey: "b",
|
locationAnnotationKey: "b",
|
||||||
persistentKeepaliveKey: "25",
|
persistentKeepaliveKey: "25",
|
||||||
@@ -205,13 +273,13 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
RegionLabelKey: "a",
|
RegionLabelKey: "a",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")}, Port: 51820},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.1"), 51820),
|
||||||
InternalIP: nil,
|
InternalIP: nil,
|
||||||
Key: []byte("foo"),
|
Key: fooKey,
|
||||||
LastSeen: 1000000000,
|
LastSeen: 1000000000,
|
||||||
Leader: false,
|
Leader: false,
|
||||||
Location: "b",
|
Location: "b",
|
||||||
PersistentKeepalive: 25,
|
PersistentKeepalive: 25 * time.Second,
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
@@ -223,7 +291,7 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
endpointAnnotationKey: "10.0.0.1:51820",
|
endpointAnnotationKey: "10.0.0.1:51820",
|
||||||
internalIPAnnotationKey: "10.1.0.1/32",
|
internalIPAnnotationKey: "10.1.0.1/32",
|
||||||
forceInternalIPAnnotationKey: "",
|
forceInternalIPAnnotationKey: "",
|
||||||
keyAnnotationKey: "foo",
|
keyAnnotationKey: fooKey.String(),
|
||||||
lastSeenAnnotationKey: "1000000000",
|
lastSeenAnnotationKey: "1000000000",
|
||||||
locationAnnotationKey: "b",
|
locationAnnotationKey: "b",
|
||||||
persistentKeepaliveKey: "25",
|
persistentKeepaliveKey: "25",
|
||||||
@@ -233,14 +301,14 @@ func TestTranslateNode(t *testing.T) {
|
|||||||
RegionLabelKey: "a",
|
RegionLabelKey: "a",
|
||||||
},
|
},
|
||||||
out: &mesh.Node{
|
out: &mesh.Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")}, Port: 51820},
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.1"), 51820),
|
||||||
NoInternalIP: true,
|
NoInternalIP: true,
|
||||||
InternalIP: nil,
|
InternalIP: nil,
|
||||||
Key: []byte("foo"),
|
Key: fooKey,
|
||||||
LastSeen: 1000000000,
|
LastSeen: 1000000000,
|
||||||
Leader: false,
|
Leader: false,
|
||||||
Location: "b",
|
Location: "b",
|
||||||
PersistentKeepalive: 25,
|
PersistentKeepalive: 25 * time.Second,
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
@@ -266,7 +334,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid ips",
|
name: "invalid ips",
|
||||||
@@ -276,7 +350,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
"foo",
|
"foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid ips",
|
name: "valid ips",
|
||||||
@@ -288,10 +368,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
AllowedIPs: []*net.IPNet{
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(32, 32)},
|
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(32, 32)},
|
||||||
},
|
},
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -305,7 +388,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
Port: mesh.DefaultKiloPort,
|
Port: mesh.DefaultKiloPort,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only endpoint port",
|
name: "only endpoint port",
|
||||||
@@ -314,7 +403,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
Port: mesh.DefaultKiloPort,
|
Port: mesh.DefaultKiloPort,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid endpoint ip",
|
name: "valid endpoint ip",
|
||||||
@@ -328,11 +423,30 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
Endpoint: &wireguard.Endpoint{
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")},
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("10.0.0.1").To4(), mesh.DefaultKiloPort),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid endpoint ipv6",
|
||||||
|
spec: v1alpha1.PeerSpec{
|
||||||
|
Endpoint: &v1alpha1.PeerEndpoint{
|
||||||
|
DNSOrIP: v1alpha1.DNSOrIP{
|
||||||
|
IP: "ff60::2",
|
||||||
|
},
|
||||||
Port: mesh.DefaultKiloPort,
|
Port: mesh.DefaultKiloPort,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("ff60::2").To16(), mesh.DefaultKiloPort),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -347,9 +461,9 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
Endpoint: &wireguard.Endpoint{
|
Endpoint: wireguard.ParseEndpoint("example.com:51820"),
|
||||||
DNSOrIP: wireguard.DNSOrIP{DNS: "example.com"},
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
Port: mesh.DefaultKiloPort,
|
PersistentKeepaliveInterval: &zero,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -359,16 +473,25 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
spec: v1alpha1.PeerSpec{
|
spec: v1alpha1.PeerSpec{
|
||||||
PublicKey: "",
|
PublicKey: "",
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid key",
|
name: "valid key",
|
||||||
spec: v1alpha1.PeerSpec{
|
spec: v1alpha1.PeerSpec{
|
||||||
PublicKey: "foo",
|
PublicKey: fooKey.String(),
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("foo"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PublicKey: fooKey,
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -377,7 +500,13 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
spec: v1alpha1.PeerSpec{
|
spec: v1alpha1.PeerSpec{
|
||||||
PersistentKeepalive: -1,
|
PersistentKeepalive: -1,
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{},
|
out: &mesh.Peer{
|
||||||
|
Peer: wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid keepalive",
|
name: "valid keepalive",
|
||||||
@@ -386,18 +515,23 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PersistentKeepalive: 1,
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &second,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid preshared key",
|
name: "valid preshared key",
|
||||||
spec: v1alpha1.PeerSpec{
|
spec: v1alpha1.PeerSpec{
|
||||||
PresharedKey: "psk",
|
PresharedKey: pskKey.String(),
|
||||||
},
|
},
|
||||||
out: &mesh.Peer{
|
out: &mesh.Peer{
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PresharedKey: []byte("psk"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PersistentKeepaliveInterval: &zero,
|
||||||
|
PresharedKey: pskKey,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -410,52 +544,3 @@ func TestTranslatePeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseEndpoint(t *testing.T) {
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
endpoint string
|
|
||||||
out *wireguard.Endpoint
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
endpoint: "",
|
|
||||||
out: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid IP",
|
|
||||||
endpoint: "10.0.0.:51820",
|
|
||||||
out: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid hostname",
|
|
||||||
endpoint: "foo-:51820",
|
|
||||||
out: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid port",
|
|
||||||
endpoint: "10.0.0.1:100000000",
|
|
||||||
out: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid IP",
|
|
||||||
endpoint: "10.0.0.1:51820",
|
|
||||||
out: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("10.0.0.1")}, Port: mesh.DefaultKiloPort},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid IPv6",
|
|
||||||
endpoint: "[ff02::114]:51820",
|
|
||||||
out: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("ff02::114")}, Port: mesh.DefaultKiloPort},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid hostname",
|
|
||||||
endpoint: "foo:51821",
|
|
||||||
out: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{DNS: "foo"}, Port: 51821},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
endpoint := parseEndpoint(tc.endpoint)
|
|
||||||
if diff := pretty.Compare(endpoint, tc.out); diff != "" {
|
|
||||||
t.Errorf("test case %q: got diff: %v", tc.name, diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 the Kilo authors
|
// Copyright 2022 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@@ -18,6 +18,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,7 +57,7 @@ const (
|
|||||||
// Node represents a node in the network.
|
// Node represents a node in the network.
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Endpoint *wireguard.Endpoint
|
Endpoint *wireguard.Endpoint
|
||||||
Key []byte
|
Key wgtypes.Key
|
||||||
NoInternalIP bool
|
NoInternalIP bool
|
||||||
InternalIP *net.IPNet
|
InternalIP *net.IPNet
|
||||||
// LastSeen is a Unix time for the last time
|
// LastSeen is a Unix time for the last time
|
||||||
@@ -66,18 +68,23 @@ type Node struct {
|
|||||||
Leader bool
|
Leader bool
|
||||||
Location string
|
Location string
|
||||||
Name string
|
Name string
|
||||||
PersistentKeepalive int
|
PersistentKeepalive time.Duration
|
||||||
Subnet *net.IPNet
|
Subnet *net.IPNet
|
||||||
WireGuardIP *net.IPNet
|
WireGuardIP *net.IPNet
|
||||||
DiscoveredEndpoints map[string]*wireguard.Endpoint
|
// DiscoveredEndpoints cannot be DNS endpoints, only net.UDPAddr.
|
||||||
AllowedLocationIPs []*net.IPNet
|
DiscoveredEndpoints map[string]*net.UDPAddr
|
||||||
|
AllowedLocationIPs []net.IPNet
|
||||||
Granularity Granularity
|
Granularity Granularity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ready indicates whether or not the node is ready.
|
// Ready indicates whether or not the node is ready.
|
||||||
func (n *Node) Ready() bool {
|
func (n *Node) Ready() bool {
|
||||||
// Nodes that are not leaders will not have WireGuardIPs, so it is not required.
|
// Nodes that are not leaders will not have WireGuardIPs, so it is not required.
|
||||||
return n != nil && n.Endpoint != nil && !(n.Endpoint.IP == nil && n.Endpoint.DNS == "") && n.Endpoint.Port != 0 && n.Key != nil && n.Subnet != nil && time.Now().Unix()-n.LastSeen < int64(checkInPeriod)*2/int64(time.Second)
|
return n != nil &&
|
||||||
|
n.Endpoint.Ready() &&
|
||||||
|
n.Key != wgtypes.Key{} &&
|
||||||
|
n.Subnet != nil &&
|
||||||
|
time.Now().Unix()-n.LastSeen < int64(checkInPeriod)*2/int64(time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer represents a peer in the network.
|
// Peer represents a peer in the network.
|
||||||
@@ -92,7 +99,10 @@ type Peer struct {
|
|||||||
// will not declare their endpoint and instead allow it to be
|
// will not declare their endpoint and instead allow it to be
|
||||||
// discovered.
|
// discovered.
|
||||||
func (p *Peer) Ready() bool {
|
func (p *Peer) Ready() bool {
|
||||||
return p != nil && p.AllowedIPs != nil && len(p.AllowedIPs) != 0 && p.PublicKey != nil
|
return p != nil &&
|
||||||
|
p.AllowedIPs != nil &&
|
||||||
|
len(p.AllowedIPs) != 0 &&
|
||||||
|
p.PublicKey != wgtypes.Key{} // If Key was not set, it will be wgtypes.Key{}.
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventType describes what kind of an action an event represents.
|
// EventType describes what kind of an action an event represents.
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mesh
|
package mesh
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mesh
|
package mesh
|
||||||
@@ -59,6 +60,7 @@ func getIP(hostname string, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error)
|
|||||||
ignore[oneAddressCIDR(ip.IP).String()] = struct{}{}
|
ignore[oneAddressCIDR(ip.IP).String()] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostPriv, hostPub []*net.IPNet
|
var hostPriv, hostPub []*net.IPNet
|
||||||
{
|
{
|
||||||
// Check IPs to which hostname resolves first.
|
// Check IPs to which hostname resolves first.
|
||||||
@@ -71,6 +73,9 @@ func getIP(hostname string, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error)
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if isLocal(ip.IP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
ip.Mask = mask
|
ip.Mask = mask
|
||||||
if isPublic(ip.IP) {
|
if isPublic(ip.IP) {
|
||||||
hostPub = append(hostPub, ip)
|
hostPub = append(hostPub, ip)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awalterschulze/gographviz"
|
"github.com/awalterschulze/gographviz"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -166,8 +167,9 @@ func nodeLabel(location, name string, cidr *net.IPNet, priv, wgIP net.IP, endpoi
|
|||||||
if wgIP != nil {
|
if wgIP != nil {
|
||||||
label = append(label, wgIP.String())
|
label = append(label, wgIP.String())
|
||||||
}
|
}
|
||||||
if endpoint != nil {
|
str := endpoint.String()
|
||||||
label = append(label, endpoint.String())
|
if str != "" {
|
||||||
|
label = append(label, str)
|
||||||
}
|
}
|
||||||
return graphEscape(strings.Join(label, "\\n"))
|
return graphEscape(strings.Join(label, "\\n"))
|
||||||
}
|
}
|
||||||
|
186
pkg/mesh/mesh.go
186
pkg/mesh/mesh.go
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mesh
|
package mesh
|
||||||
@@ -29,6 +30,8 @@ import (
|
|||||||
"github.com/go-kit/kit/log/level"
|
"github.com/go-kit/kit/log/level"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/encapsulation"
|
"github.com/squat/kilo/pkg/encapsulation"
|
||||||
"github.com/squat/kilo/pkg/iproute"
|
"github.com/squat/kilo/pkg/iproute"
|
||||||
@@ -42,8 +45,6 @@ const (
|
|||||||
kiloPath = "/var/lib/kilo"
|
kiloPath = "/var/lib/kilo"
|
||||||
// privateKeyPath is the filepath where the WireGuard private key is stored.
|
// privateKeyPath is the filepath where the WireGuard private key is stored.
|
||||||
privateKeyPath = kiloPath + "/key"
|
privateKeyPath = kiloPath + "/key"
|
||||||
// confPath is the filepath where the WireGuard configuration is stored.
|
|
||||||
confPath = kiloPath + "/conf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mesh is able to create Kilo network meshes.
|
// Mesh is able to create Kilo network meshes.
|
||||||
@@ -59,13 +60,15 @@ type Mesh struct {
|
|||||||
internalIP *net.IPNet
|
internalIP *net.IPNet
|
||||||
ipTables *iptables.Controller
|
ipTables *iptables.Controller
|
||||||
kiloIface int
|
kiloIface int
|
||||||
|
kiloIfaceName string
|
||||||
key []byte
|
key []byte
|
||||||
local bool
|
local bool
|
||||||
port uint32
|
port int
|
||||||
priv []byte
|
priv wgtypes.Key
|
||||||
privIface int
|
privIface int
|
||||||
pub []byte
|
pub wgtypes.Key
|
||||||
resyncPeriod time.Duration
|
resyncPeriod time.Duration
|
||||||
|
iptablesForwardRule bool
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
subnet *net.IPNet
|
subnet *net.IPNet
|
||||||
table *route.Table
|
table *route.Table
|
||||||
@@ -86,23 +89,24 @@ type Mesh struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Mesh instance.
|
// New returns a new Mesh instance.
|
||||||
func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularity, hostname string, port uint32, subnet *net.IPNet, local, cni bool, cniPath, iface string, cleanUpIface bool, createIface bool, mtu uint, resyncPeriod time.Duration, logger log.Logger) (*Mesh, error) {
|
func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularity, hostname string, port int, subnet *net.IPNet, local, cni bool, cniPath, iface string, cleanUpIface bool, createIface bool, mtu uint, resyncPeriod time.Duration, prioritisePrivateAddr, iptablesForwardRule bool, logger log.Logger) (*Mesh, error) {
|
||||||
if err := os.MkdirAll(kiloPath, 0700); err != nil {
|
if err := os.MkdirAll(kiloPath, 0700); err != nil {
|
||||||
return nil, fmt.Errorf("failed to create directory to store configuration: %v", err)
|
return nil, fmt.Errorf("failed to create directory to store configuration: %v", err)
|
||||||
}
|
}
|
||||||
private, err := ioutil.ReadFile(privateKeyPath)
|
privateB, err := ioutil.ReadFile(privateKeyPath)
|
||||||
private = bytes.Trim(private, "\n")
|
privateB = bytes.Trim(privateB, "\n")
|
||||||
|
private, err := wgtypes.ParseKey(string(privateB))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Warn(logger).Log("msg", "no private key found on disk; generating one now")
|
level.Warn(logger).Log("msg", "no private key found on disk; generating one now")
|
||||||
if private, err = wireguard.GenKey(); err != nil {
|
if private, err = wgtypes.GeneratePrivateKey(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public, err := wireguard.PubKey(private)
|
public := private.PublicKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(privateKeyPath, private, 0600); err != nil {
|
if err := ioutil.WriteFile(privateKeyPath, []byte(private.String()), 0600); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write private key to disk: %v", err)
|
return nil, fmt.Errorf("failed to write private key to disk: %v", err)
|
||||||
}
|
}
|
||||||
cniIndex, err := cniDeviceIndex()
|
cniIndex, err := cniDeviceIndex()
|
||||||
@@ -143,6 +147,12 @@ func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularit
|
|||||||
enc = encapsulation.Noop(enc.Strategy())
|
enc = encapsulation.Noop(enc.Strategy())
|
||||||
level.Debug(logger).Log("msg", "running without a private IP address")
|
level.Debug(logger).Log("msg", "running without a private IP address")
|
||||||
}
|
}
|
||||||
|
var externalIP *net.IPNet
|
||||||
|
if prioritisePrivateAddr && privateIP != nil {
|
||||||
|
externalIP = privateIP
|
||||||
|
} else {
|
||||||
|
externalIP = publicIP
|
||||||
|
}
|
||||||
level.Debug(logger).Log("msg", fmt.Sprintf("using %s as the public IP address", publicIP.String()))
|
level.Debug(logger).Log("msg", fmt.Sprintf("using %s as the public IP address", publicIP.String()))
|
||||||
ipTables, err := iptables.New(iptables.WithLogger(log.With(logger, "component", "iptables")), iptables.WithResyncPeriod(resyncPeriod))
|
ipTables, err := iptables.New(iptables.WithLogger(log.With(logger, "component", "iptables")), iptables.WithResyncPeriod(resyncPeriod))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -154,12 +164,13 @@ func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularit
|
|||||||
cni: cni,
|
cni: cni,
|
||||||
cniPath: cniPath,
|
cniPath: cniPath,
|
||||||
enc: enc,
|
enc: enc,
|
||||||
externalIP: publicIP,
|
externalIP: externalIP,
|
||||||
granularity: granularity,
|
granularity: granularity,
|
||||||
hostname: hostname,
|
hostname: hostname,
|
||||||
internalIP: privateIP,
|
internalIP: privateIP,
|
||||||
ipTables: ipTables,
|
ipTables: ipTables,
|
||||||
kiloIface: kiloIface,
|
kiloIface: kiloIface,
|
||||||
|
kiloIfaceName: iface,
|
||||||
nodes: make(map[string]*Node),
|
nodes: make(map[string]*Node),
|
||||||
peers: make(map[string]*Peer),
|
peers: make(map[string]*Peer),
|
||||||
port: port,
|
port: port,
|
||||||
@@ -167,6 +178,7 @@ func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularit
|
|||||||
privIface: privIface,
|
privIface: privIface,
|
||||||
pub: public,
|
pub: public,
|
||||||
resyncPeriod: resyncPeriod,
|
resyncPeriod: resyncPeriod,
|
||||||
|
iptablesForwardRule: iptablesForwardRule,
|
||||||
local: local,
|
local: local,
|
||||||
stop: make(chan struct{}),
|
stop: make(chan struct{}),
|
||||||
subnet: subnet,
|
subnet: subnet,
|
||||||
@@ -305,7 +317,7 @@ func (m *Mesh) syncPeers(e *PeerEvent) {
|
|||||||
var diff bool
|
var diff bool
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
// Peers are indexed by public key.
|
// Peers are indexed by public key.
|
||||||
key := string(e.Peer.PublicKey)
|
key := e.Peer.PublicKey.String()
|
||||||
if !e.Peer.Ready() {
|
if !e.Peer.Ready() {
|
||||||
// Trace non ready peer with their presence in the mesh.
|
// Trace non ready peer with their presence in the mesh.
|
||||||
_, ok := m.peers[key]
|
_, ok := m.peers[key]
|
||||||
@@ -315,8 +327,8 @@ func (m *Mesh) syncPeers(e *PeerEvent) {
|
|||||||
case AddEvent:
|
case AddEvent:
|
||||||
fallthrough
|
fallthrough
|
||||||
case UpdateEvent:
|
case UpdateEvent:
|
||||||
if e.Old != nil && key != string(e.Old.PublicKey) {
|
if e.Old != nil && key != e.Old.PublicKey.String() {
|
||||||
delete(m.peers, string(e.Old.PublicKey))
|
delete(m.peers, e.Old.PublicKey.String())
|
||||||
diff = true
|
diff = true
|
||||||
}
|
}
|
||||||
if !peersAreEqual(m.peers[key], e.Peer) {
|
if !peersAreEqual(m.peers[key], e.Peer) {
|
||||||
@@ -358,8 +370,10 @@ func (m *Mesh) checkIn() {
|
|||||||
|
|
||||||
func (m *Mesh) handleLocal(n *Node) {
|
func (m *Mesh) handleLocal(n *Node) {
|
||||||
// Allow the IPs to be overridden.
|
// Allow the IPs to be overridden.
|
||||||
if n.Endpoint == nil || (n.Endpoint.DNS == "" && n.Endpoint.IP == nil) {
|
if !n.Endpoint.Ready() {
|
||||||
n.Endpoint = &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: m.externalIP.IP}, Port: m.port}
|
e := wireguard.NewEndpoint(m.externalIP.IP, m.port)
|
||||||
|
level.Info(m.logger).Log("msg", "overriding endpoint", "node", m.hostname, "old endpoint", n.Endpoint.String(), "new endpoint", e.String())
|
||||||
|
n.Endpoint = e
|
||||||
}
|
}
|
||||||
if n.InternalIP == nil && !n.NoInternalIP {
|
if n.InternalIP == nil && !n.NoInternalIP {
|
||||||
n.InternalIP = m.internalIP
|
n.InternalIP = m.internalIP
|
||||||
@@ -453,22 +467,26 @@ func (m *Mesh) applyTopology() {
|
|||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Find the old configuration.
|
|
||||||
oldConfDump, err := wireguard.ShowDump(link.Attrs().Name)
|
wgClient, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
oldConf, err := wireguard.ParseDump(oldConfDump)
|
defer wgClient.Close()
|
||||||
|
|
||||||
|
// wgDevice is the current configuration of the wg interface.
|
||||||
|
wgDevice, err := wgClient.Device(m.kiloIfaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
natEndpoints := discoverNATEndpoints(nodes, peers, oldConf, m.logger)
|
|
||||||
|
natEndpoints := discoverNATEndpoints(nodes, peers, wgDevice, m.logger)
|
||||||
nodes[m.hostname].DiscoveredEndpoints = natEndpoints
|
nodes[m.hostname].DiscoveredEndpoints = natEndpoints
|
||||||
t, err := NewTopology(nodes, peers, m.granularity, m.hostname, nodes[m.hostname].Endpoint.Port, m.priv, m.subnet, nodes[m.hostname].PersistentKeepalive, m.logger)
|
t, err := NewTopology(nodes, peers, m.granularity, m.hostname, nodes[m.hostname].Endpoint.Port(), m.priv, m.subnet, nodes[m.hostname].PersistentKeepalive, m.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
@@ -480,19 +498,8 @@ func (m *Mesh) applyTopology() {
|
|||||||
} else {
|
} else {
|
||||||
m.wireGuardIP = nil
|
m.wireGuardIP = nil
|
||||||
}
|
}
|
||||||
conf := t.Conf()
|
ipRules := t.Rules(m.cni, m.iptablesForwardRule)
|
||||||
buf, err := conf.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
level.Error(m.logger).Log("error", err)
|
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(confPath, buf, 0600); err != nil {
|
|
||||||
level.Error(m.logger).Log("error", err)
|
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ipRules := t.Rules(m.cni)
|
|
||||||
// If we are handling local routes, ensure the local
|
// If we are handling local routes, ensure the local
|
||||||
// tunnel has an IP address and IPIP traffic is allowed.
|
// tunnel has an IP address and IPIP traffic is allowed.
|
||||||
if m.enc.Strategy() != encapsulation.Never && m.local {
|
if m.enc.Strategy() != encapsulation.Never && m.local {
|
||||||
@@ -531,10 +538,12 @@ func (m *Mesh) applyTopology() {
|
|||||||
}
|
}
|
||||||
// Setting the WireGuard configuration interrupts existing connections
|
// Setting the WireGuard configuration interrupts existing connections
|
||||||
// so only set the configuration if it has changed.
|
// so only set the configuration if it has changed.
|
||||||
equal := conf.Equal(oldConf)
|
conf := t.Conf()
|
||||||
|
equal, diff := conf.Equal(wgDevice)
|
||||||
if !equal {
|
if !equal {
|
||||||
level.Info(m.logger).Log("msg", "WireGuard configurations are different")
|
level.Info(m.logger).Log("msg", "WireGuard configurations are different", "diff", diff)
|
||||||
if err := wireguard.SetConf(link.Attrs().Name, confPath); err != nil {
|
level.Debug(m.logger).Log("msg", "changing wg config", "config", conf.WGConfig())
|
||||||
|
if err := wgClient.ConfigureDevice(m.kiloIfaceName, conf.WGConfig()); err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
return
|
return
|
||||||
@@ -589,10 +598,6 @@ func (m *Mesh) cleanUp() {
|
|||||||
level.Error(m.logger).Log("error", fmt.Sprintf("failed to clean up routes: %v", err))
|
level.Error(m.logger).Log("error", fmt.Sprintf("failed to clean up routes: %v", err))
|
||||||
m.errorCounter.WithLabelValues("cleanUp").Inc()
|
m.errorCounter.WithLabelValues("cleanUp").Inc()
|
||||||
}
|
}
|
||||||
if err := os.Remove(confPath); err != nil {
|
|
||||||
level.Error(m.logger).Log("error", fmt.Sprintf("failed to delete configuration file: %v", err))
|
|
||||||
m.errorCounter.WithLabelValues("cleanUp").Inc()
|
|
||||||
}
|
|
||||||
if m.cleanUpIface {
|
if m.cleanUpIface {
|
||||||
if err := iproute.RemoveInterface(m.kiloIface); err != nil {
|
if err := iproute.RemoveInterface(m.kiloIface); err != nil {
|
||||||
level.Error(m.logger).Log("error", fmt.Sprintf("failed to remove WireGuard interface: %v", err))
|
level.Error(m.logger).Log("error", fmt.Sprintf("failed to remove WireGuard interface: %v", err))
|
||||||
@@ -620,12 +625,8 @@ func (m *Mesh) resolveEndpoints() error {
|
|||||||
if !m.nodes[k].Ready() {
|
if !m.nodes[k].Ready() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If the node is ready, then the endpoint is not nil
|
// Resolve the Endpoint
|
||||||
// but it may not have a DNS name.
|
if _, err := m.nodes[k].Endpoint.UDPAddr(true); err != nil {
|
||||||
if m.nodes[k].Endpoint.DNS == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := resolveEndpoint(m.nodes[k].Endpoint); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,33 +637,16 @@ func (m *Mesh) resolveEndpoints() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Peers may have nil endpoints.
|
// Peers may have nil endpoints.
|
||||||
if m.peers[k].Endpoint == nil || m.peers[k].Endpoint.DNS == "" {
|
if !m.peers[k].Endpoint.Ready() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := resolveEndpoint(m.peers[k].Endpoint); err != nil {
|
if _, err := m.peers[k].Endpoint.UDPAddr(true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveEndpoint(endpoint *wireguard.Endpoint) error {
|
|
||||||
ips, err := net.LookupIP(endpoint.DNS)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to look up DNS name %q: %v", endpoint.DNS, err)
|
|
||||||
}
|
|
||||||
nets := make([]*net.IPNet, len(ips), len(ips))
|
|
||||||
for i := range ips {
|
|
||||||
nets[i] = oneAddressCIDR(ips[i])
|
|
||||||
}
|
|
||||||
sortIPs(nets)
|
|
||||||
if len(nets) == 0 {
|
|
||||||
return fmt.Errorf("did not find any addresses for DNS name %q", endpoint.DNS)
|
|
||||||
}
|
|
||||||
endpoint.IP = nets[0].IP
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSelf(hostname string, node *Node) bool {
|
func isSelf(hostname string, node *Node) bool {
|
||||||
return node != nil && node.Name == hostname
|
return node != nil && node.Name == hostname
|
||||||
}
|
}
|
||||||
@@ -682,7 +666,18 @@ func nodesAreEqual(a, b *Node) bool {
|
|||||||
// Ignore LastSeen when comparing equality we want to check if the nodes are
|
// Ignore LastSeen when comparing equality we want to check if the nodes are
|
||||||
// equivalent. However, we do want to check if LastSeen has transitioned
|
// equivalent. However, we do want to check if LastSeen has transitioned
|
||||||
// between valid and invalid.
|
// between valid and invalid.
|
||||||
return string(a.Key) == string(b.Key) && ipNetsEqual(a.WireGuardIP, b.WireGuardIP) && ipNetsEqual(a.InternalIP, b.InternalIP) && a.Leader == b.Leader && a.Location == b.Location && a.Name == b.Name && subnetsEqual(a.Subnet, b.Subnet) && a.Ready() == b.Ready() && a.PersistentKeepalive == b.PersistentKeepalive && discoveredEndpointsAreEqual(a.DiscoveredEndpoints, b.DiscoveredEndpoints) && ipNetSlicesEqual(a.AllowedLocationIPs, b.AllowedLocationIPs) && a.Granularity == b.Granularity
|
return a.Key.String() == b.Key.String() &&
|
||||||
|
ipNetsEqual(a.WireGuardIP, b.WireGuardIP) &&
|
||||||
|
ipNetsEqual(a.InternalIP, b.InternalIP) &&
|
||||||
|
a.Leader == b.Leader &&
|
||||||
|
a.Location == b.Location &&
|
||||||
|
a.Name == b.Name &&
|
||||||
|
subnetsEqual(a.Subnet, b.Subnet) &&
|
||||||
|
a.Ready() == b.Ready() &&
|
||||||
|
a.PersistentKeepalive == b.PersistentKeepalive &&
|
||||||
|
discoveredEndpointsAreEqual(a.DiscoveredEndpoints, b.DiscoveredEndpoints) &&
|
||||||
|
ipNetSlicesEqual(a.AllowedLocationIPs, b.AllowedLocationIPs) &&
|
||||||
|
a.Granularity == b.Granularity
|
||||||
}
|
}
|
||||||
|
|
||||||
func peersAreEqual(a, b *Peer) bool {
|
func peersAreEqual(a, b *Peer) bool {
|
||||||
@@ -701,11 +696,15 @@ func peersAreEqual(a, b *Peer) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := range a.AllowedIPs {
|
for i := range a.AllowedIPs {
|
||||||
if !ipNetsEqual(a.AllowedIPs[i], b.AllowedIPs[i]) {
|
if !ipNetsEqual(&a.AllowedIPs[i], &b.AllowedIPs[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string(a.PublicKey) == string(b.PublicKey) && string(a.PresharedKey) == string(b.PresharedKey) && a.PersistentKeepalive == b.PersistentKeepalive
|
return a.PublicKey.String() == b.PublicKey.String() &&
|
||||||
|
(a.PresharedKey == nil) == (b.PresharedKey == nil) &&
|
||||||
|
(a.PresharedKey == nil || a.PresharedKey.String() == b.PresharedKey.String()) &&
|
||||||
|
(a.PersistentKeepaliveInterval == nil) == (b.PersistentKeepaliveInterval == nil) &&
|
||||||
|
(a.PersistentKeepaliveInterval == nil || *a.PersistentKeepaliveInterval == *b.PersistentKeepaliveInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipNetsEqual(a, b *net.IPNet) bool {
|
func ipNetsEqual(a, b *net.IPNet) bool {
|
||||||
@@ -721,12 +720,12 @@ func ipNetsEqual(a, b *net.IPNet) bool {
|
|||||||
return a.IP.Equal(b.IP)
|
return a.IP.Equal(b.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipNetSlicesEqual(a, b []*net.IPNet) bool {
|
func ipNetSlicesEqual(a, b []net.IPNet) bool {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := range a {
|
for i := range a {
|
||||||
if !ipNetsEqual(a[i], b[i]) {
|
if !ipNetsEqual(&a[i], &b[i]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -752,18 +751,31 @@ func subnetsEqual(a, b *net.IPNet) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func discoveredEndpointsAreEqual(a, b map[string]*wireguard.Endpoint) bool {
|
func udpAddrsEqual(a, b *net.UDPAddr) bool {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (a != nil) != (b != nil) {
|
if (a != nil) != (b != nil) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if a.Zone != b.Zone {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Port != b.Port {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return a.IP.Equal(b.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func discoveredEndpointsAreEqual(a, b map[string]*net.UDPAddr) bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for k := range a {
|
for k := range a {
|
||||||
if !a[k].Equal(b[k], false) {
|
if !udpAddrsEqual(a[k], b[k]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -779,24 +791,26 @@ func linkByIndex(index int) (netlink.Link, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// discoverNATEndpoints uses the node's WireGuard configuration to returns a list of the most recently discovered endpoints for all nodes and peers behind NAT so that they can roam.
|
// discoverNATEndpoints uses the node's WireGuard configuration to returns a list of the most recently discovered endpoints for all nodes and peers behind NAT so that they can roam.
|
||||||
func discoverNATEndpoints(nodes map[string]*Node, peers map[string]*Peer, conf *wireguard.Conf, logger log.Logger) map[string]*wireguard.Endpoint {
|
// Discovered endpionts will never be DNS names, because WireGuard will always resolve them to net.UDPAddr.
|
||||||
natEndpoints := make(map[string]*wireguard.Endpoint)
|
func discoverNATEndpoints(nodes map[string]*Node, peers map[string]*Peer, conf *wgtypes.Device, logger log.Logger) map[string]*net.UDPAddr {
|
||||||
keys := make(map[string]*wireguard.Peer)
|
natEndpoints := make(map[string]*net.UDPAddr)
|
||||||
|
keys := make(map[string]wgtypes.Peer)
|
||||||
for i := range conf.Peers {
|
for i := range conf.Peers {
|
||||||
keys[string(conf.Peers[i].PublicKey)] = conf.Peers[i]
|
keys[conf.Peers[i].PublicKey.String()] = conf.Peers[i]
|
||||||
}
|
}
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
if peer, ok := keys[string(n.Key)]; ok && n.PersistentKeepalive > 0 {
|
if peer, ok := keys[n.Key.String()]; ok && n.PersistentKeepalive != time.Duration(0) {
|
||||||
level.Debug(logger).Log("msg", "WireGuard Update NAT Endpoint", "node", n.Name, "endpoint", peer.Endpoint, "former-endpoint", n.Endpoint, "same", n.Endpoint.Equal(peer.Endpoint, false), "latest-handshake", peer.LatestHandshake)
|
level.Debug(logger).Log("msg", "WireGuard Update NAT Endpoint", "node", n.Name, "endpoint", peer.Endpoint, "former-endpoint", n.Endpoint, "same", peer.Endpoint.String() == n.Endpoint.String(), "latest-handshake", peer.LastHandshakeTime)
|
||||||
if (peer.LatestHandshake != time.Time{}) {
|
// Don't update the endpoint, if there was never any handshake.
|
||||||
natEndpoints[string(n.Key)] = peer.Endpoint
|
if !peer.LastHandshakeTime.Equal(time.Time{}) {
|
||||||
|
natEndpoints[n.Key.String()] = peer.Endpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
if peer, ok := keys[string(p.PublicKey)]; ok && p.PersistentKeepalive > 0 {
|
if peer, ok := keys[p.PublicKey.String()]; ok && p.PersistentKeepaliveInterval != nil {
|
||||||
if (peer.LatestHandshake != time.Time{}) {
|
if !peer.LastHandshakeTime.Equal(time.Time{}) {
|
||||||
natEndpoints[string(p.PublicKey)] = peer.Endpoint
|
natEndpoints[p.PublicKey.String()] = peer.Endpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,9 +19,21 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mustKey() wgtypes.Key {
|
||||||
|
if k, err := wgtypes.GeneratePrivateKey(); err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
} else {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = mustKey()
|
||||||
|
|
||||||
func TestReady(t *testing.T) {
|
func TestReady(t *testing.T) {
|
||||||
internalIP := oneAddressCIDR(net.ParseIP("1.1.1.1"))
|
internalIP := oneAddressCIDR(net.ParseIP("1.1.1.1"))
|
||||||
externalIP := oneAddressCIDR(net.ParseIP("2.2.2.2"))
|
externalIP := oneAddressCIDR(net.ParseIP("2.2.2.2"))
|
||||||
@@ -44,7 +56,7 @@ func TestReady(t *testing.T) {
|
|||||||
name: "empty endpoint",
|
name: "empty endpoint",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte{},
|
Key: key,
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
ready: false,
|
ready: false,
|
||||||
@@ -52,9 +64,9 @@ func TestReady(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty endpoint IP",
|
name: "empty endpoint IP",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(nil, DefaultKiloPort),
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte{},
|
Key: wgtypes.Key{},
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
ready: false,
|
ready: false,
|
||||||
@@ -62,9 +74,9 @@ func TestReady(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty endpoint port",
|
name: "empty endpoint port",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: externalIP.IP}},
|
Endpoint: wireguard.NewEndpoint(externalIP.IP, 0),
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte{},
|
Key: wgtypes.Key{},
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
ready: false,
|
ready: false,
|
||||||
@@ -72,8 +84,8 @@ func TestReady(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty internal IP",
|
name: "empty internal IP",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: externalIP.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(externalIP.IP, DefaultKiloPort),
|
||||||
Key: []byte{},
|
Key: wgtypes.Key{},
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
ready: false,
|
ready: false,
|
||||||
@@ -81,7 +93,7 @@ func TestReady(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty key",
|
name: "empty key",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: externalIP.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(externalIP.IP, DefaultKiloPort),
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
@@ -90,18 +102,18 @@ func TestReady(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty subnet",
|
name: "empty subnet",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: externalIP.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(externalIP.IP, DefaultKiloPort),
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte{},
|
Key: wgtypes.Key{},
|
||||||
},
|
},
|
||||||
ready: false,
|
ready: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
node: &Node{
|
node: &Node{
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: externalIP.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(externalIP.IP, DefaultKiloPort),
|
||||||
InternalIP: internalIP,
|
InternalIP: internalIP,
|
||||||
Key: []byte{},
|
Key: key,
|
||||||
LastSeen: time.Now().Unix(),
|
LastSeen: time.Now().Unix(),
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.0.0"), Mask: net.CIDRMask(16, 32)},
|
||||||
},
|
},
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package mesh
|
package mesh
|
||||||
@@ -39,7 +40,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
var gw net.IP
|
var gw net.IP
|
||||||
for _, segment := range t.segments {
|
for _, segment := range t.segments {
|
||||||
if segment.location == t.location {
|
if segment.location == t.location {
|
||||||
gw = enc.Gw(segment.endpoint.IP, segment.privateIPs[segment.leader], segment.cidrs[segment.leader])
|
gw = enc.Gw(t.updateEndpoint(segment.endpoint, segment.key, &segment.persistentKeepalive).IP(), segment.privateIPs[segment.leader], segment.cidrs[segment.leader])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
// we need to set routes for allowed location IPs over the leader in the current location.
|
// we need to set routes for allowed location IPs over the leader in the current location.
|
||||||
for i := range segment.allowedLocationIPs {
|
for i := range segment.allowedLocationIPs {
|
||||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||||
Dst: segment.allowedLocationIPs[i],
|
Dst: &segment.allowedLocationIPs[i],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: gw,
|
Gw: gw,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
@@ -124,7 +125,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
for _, peer := range t.peers {
|
for _, peer := range t.peers {
|
||||||
for i := range peer.AllowedIPs {
|
for i := range peer.AllowedIPs {
|
||||||
routes = append(routes, encapsulateRoute(&netlink.Route{
|
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||||
Dst: peer.AllowedIPs[i],
|
Dst: &peer.AllowedIPs[i],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: gw,
|
Gw: gw,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
@@ -195,7 +196,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
// equals the external IP. This means that the node
|
// equals the external IP. This means that the node
|
||||||
// is only accessible through an external IP and we
|
// is only accessible through an external IP and we
|
||||||
// cannot encapsulate traffic to an IP through the IP.
|
// cannot encapsulate traffic to an IP through the IP.
|
||||||
if segment.privateIPs == nil || segment.privateIPs[i].Equal(segment.endpoint.IP) {
|
if segment.privateIPs == nil || segment.privateIPs[i].Equal(t.updateEndpoint(segment.endpoint, segment.key, &segment.persistentKeepalive).IP()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Add routes to the private IPs of nodes in other segments.
|
// Add routes to the private IPs of nodes in other segments.
|
||||||
@@ -213,7 +214,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
// we need to set routes for allowed location IPs over the wg interface.
|
// we need to set routes for allowed location IPs over the wg interface.
|
||||||
for i := range segment.allowedLocationIPs {
|
for i := range segment.allowedLocationIPs {
|
||||||
routes = append(routes, &netlink.Route{
|
routes = append(routes, &netlink.Route{
|
||||||
Dst: segment.allowedLocationIPs[i],
|
Dst: &segment.allowedLocationIPs[i],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: segment.wireGuardIP,
|
Gw: segment.wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -225,7 +226,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
for _, peer := range t.peers {
|
for _, peer := range t.peers {
|
||||||
for i := range peer.AllowedIPs {
|
for i := range peer.AllowedIPs {
|
||||||
routes = append(routes, &netlink.Route{
|
routes = append(routes, &netlink.Route{
|
||||||
Dst: peer.AllowedIPs[i],
|
Dst: &peer.AllowedIPs[i],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
})
|
})
|
||||||
@@ -234,6 +235,74 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
return routes, rules
|
return routes, rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerRoutes generates a slice of routes and rules for a given peer in the Topology.
|
||||||
|
func (t *Topology) PeerRoutes(name string, kiloIface int, additionalAllowedIPs []net.IPNet) ([]*netlink.Route, []*netlink.Rule) {
|
||||||
|
var routes []*netlink.Route
|
||||||
|
var rules []*netlink.Rule
|
||||||
|
for _, segment := range t.segments {
|
||||||
|
for i := range segment.cidrs {
|
||||||
|
// Add routes to the Pod CIDRs of nodes in other segments.
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: segment.cidrs[i],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: segment.wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for i := range segment.privateIPs {
|
||||||
|
// Add routes to the private IPs of nodes in other segments.
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: oneAddressCIDR(segment.privateIPs[i]),
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: segment.wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Add routes for the allowed location IPs of all segments.
|
||||||
|
for i := range segment.allowedLocationIPs {
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: &segment.allowedLocationIPs[i],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: segment.wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: oneAddressCIDR(segment.wireGuardIP),
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Add routes for the allowed IPs of peers.
|
||||||
|
for _, peer := range t.peers {
|
||||||
|
// Don't add routes to ourselves.
|
||||||
|
if peer.Name == name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i := range peer.AllowedIPs {
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: &peer.AllowedIPs[i],
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range additionalAllowedIPs {
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: &additionalAllowedIPs[i],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: t.segments[0].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return routes, rules
|
||||||
|
}
|
||||||
|
|
||||||
func encapsulateRoute(route *netlink.Route, encapsulate encapsulation.Strategy, subnet *net.IPNet, tunlIface int) *netlink.Route {
|
func encapsulateRoute(route *netlink.Route, encapsulate encapsulation.Strategy, subnet *net.IPNet, tunlIface int) *netlink.Route {
|
||||||
if encapsulate == encapsulation.Always || (encapsulate == encapsulation.CrossSubnet && !subnet.Contains(route.Gw)) {
|
if encapsulate == encapsulation.Always || (encapsulate == encapsulation.CrossSubnet && !subnet.Contains(route.Gw)) {
|
||||||
route.LinkIndex = tunlIface
|
route.LinkIndex = tunlIface
|
||||||
@@ -242,17 +311,45 @@ func encapsulateRoute(route *netlink.Route, encapsulate encapsulation.Strategy,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rules returns the iptables rules required by the local node.
|
// Rules returns the iptables rules required by the local node.
|
||||||
func (t *Topology) Rules(cni bool) []iptables.Rule {
|
func (t *Topology) Rules(cni, iptablesForwardRule bool) []iptables.Rule {
|
||||||
var rules []iptables.Rule
|
var rules []iptables.Rule
|
||||||
rules = append(rules, iptables.NewIPv4Chain("nat", "KILO-NAT"))
|
rules = append(rules, iptables.NewIPv4Chain("nat", "KILO-NAT"))
|
||||||
rules = append(rules, iptables.NewIPv6Chain("nat", "KILO-NAT"))
|
rules = append(rules, iptables.NewIPv6Chain("nat", "KILO-NAT"))
|
||||||
if cni {
|
if cni {
|
||||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(t.subnet.IP)), "nat", "POSTROUTING", "-s", t.subnet.String(), "-m", "comment", "--comment", "Kilo: jump to KILO-NAT chain", "-j", "KILO-NAT"))
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(t.subnet.IP), "nat", "POSTROUTING", "-s", t.subnet.String(), "-m", "comment", "--comment", "Kilo: jump to KILO-NAT chain", "-j", "KILO-NAT"))
|
||||||
|
// Some linux distros or docker will set forward DROP in the filter table.
|
||||||
|
// To still be able to have pod to pod communication we need to ALLOW packets from and to pod CIDRs within a location.
|
||||||
|
// Leader nodes will forward packets from all nodes within a location because they act as a gateway for them.
|
||||||
|
// Non leader nodes only need to allow packages from and to their own pod CIDR.
|
||||||
|
if iptablesForwardRule && t.leader {
|
||||||
|
for _, s := range t.segments {
|
||||||
|
if s.location == t.location {
|
||||||
|
// Make sure packets to and from pod cidrs are not dropped in the forward chain.
|
||||||
|
for _, c := range s.cidrs {
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets from the pod subnet", "-s", c.String(), "-j", "ACCEPT"))
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets to the pod subnet", "-d", c.String(), "-j", "ACCEPT"))
|
||||||
|
}
|
||||||
|
// Make sure packets to and from allowed location IPs are not dropped in the forward chain.
|
||||||
|
for _, c := range s.allowedLocationIPs {
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets from allowed location IPs", "-s", c.String(), "-j", "ACCEPT"))
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets to allowed location IPs", "-d", c.String(), "-j", "ACCEPT"))
|
||||||
|
}
|
||||||
|
// Make sure packets to and from private IPs are not dropped in the forward chain.
|
||||||
|
for _, c := range s.privateIPs {
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets from private IPs", "-s", oneAddressCIDR(c).String(), "-j", "ACCEPT"))
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(c), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets to private IPs", "-d", oneAddressCIDR(c).String(), "-j", "ACCEPT"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if iptablesForwardRule {
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(t.subnet.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets from the node's pod subnet", "-s", t.subnet.String(), "-j", "ACCEPT"))
|
||||||
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(t.subnet.IP), "filter", "FORWARD", "-m", "comment", "--comment", "Kilo: forward packets to the node's pod subnet", "-d", t.subnet.String(), "-j", "ACCEPT"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, s := range t.segments {
|
for _, s := range t.segments {
|
||||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(s.wireGuardIP)), "nat", "KILO-NAT", "-d", oneAddressCIDR(s.wireGuardIP).String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for WireGuared IPs", "-j", "RETURN"))
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(s.wireGuardIP), "nat", "KILO-NAT", "-d", oneAddressCIDR(s.wireGuardIP).String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for WireGuared IPs", "-j", "RETURN"))
|
||||||
for _, aip := range s.allowedIPs {
|
for _, aip := range s.allowedIPs {
|
||||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for known IPs", "-j", "RETURN"))
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(aip.IP), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for known IPs", "-j", "RETURN"))
|
||||||
}
|
}
|
||||||
// Make sure packets to allowed location IPs go through the KILO-NAT chain, so they can be MASQUERADEd,
|
// Make sure packets to allowed location IPs go through the KILO-NAT chain, so they can be MASQUERADEd,
|
||||||
// Otherwise packets to these destinations will reach the destination, but never find their way back.
|
// Otherwise packets to these destinations will reach the destination, but never find their way back.
|
||||||
@@ -260,7 +357,7 @@ func (t *Topology) Rules(cni bool) []iptables.Rule {
|
|||||||
if t.location == s.location {
|
if t.location == s.location {
|
||||||
for _, alip := range s.allowedLocationIPs {
|
for _, alip := range s.allowedLocationIPs {
|
||||||
rules = append(rules,
|
rules = append(rules,
|
||||||
iptables.NewRule(iptables.GetProtocol(len(alip.IP)), "nat", "POSTROUTING", "-d", alip.String(), "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-j", "KILO-NAT"),
|
iptables.NewRule(iptables.GetProtocol(alip.IP), "nat", "POSTROUTING", "-d", alip.String(), "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-j", "KILO-NAT"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,8 +365,8 @@ func (t *Topology) Rules(cni bool) []iptables.Rule {
|
|||||||
for _, p := range t.peers {
|
for _, p := range t.peers {
|
||||||
for _, aip := range p.AllowedIPs {
|
for _, aip := range p.AllowedIPs {
|
||||||
rules = append(rules,
|
rules = append(rules,
|
||||||
iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "POSTROUTING", "-s", aip.String(), "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-j", "KILO-NAT"),
|
iptables.NewRule(iptables.GetProtocol(aip.IP), "nat", "POSTROUTING", "-s", aip.String(), "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-j", "KILO-NAT"),
|
||||||
iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for peers", "-j", "RETURN"),
|
iptables.NewRule(iptables.GetProtocol(aip.IP), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for peers", "-j", "RETURN"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -75,7 +75,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -89,17 +89,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -132,17 +132,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -196,21 +196,21 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
@@ -266,24 +266,24 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["d"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["d"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -309,7 +309,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -337,17 +337,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -394,17 +394,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -444,7 +444,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -458,17 +458,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -509,7 +509,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -523,17 +523,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -574,7 +574,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -588,17 +588,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -639,17 +639,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -698,17 +698,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -782,21 +782,21 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: privIface,
|
LinkIndex: privIface,
|
||||||
@@ -868,21 +868,21 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: tunlIface,
|
LinkIndex: tunlIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: tunlIface,
|
LinkIndex: tunlIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: nodes["b"].InternalIP.IP,
|
Gw: nodes["b"].InternalIP.IP,
|
||||||
LinkIndex: tunlIface,
|
LinkIndex: tunlIface,
|
||||||
@@ -918,7 +918,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -946,17 +946,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -1004,17 +1004,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
@@ -1055,7 +1055,7 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["b"].AllowedLocationIPs[0],
|
Dst: &nodes["b"].AllowedLocationIPs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -1069,17 +1069,17 @@ func TestRoutes(t *testing.T) {
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: &peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[1],
|
Dst: &peers["a"].AllowedIPs[1],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["b"].AllowedIPs[0],
|
Dst: &peers["b"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -18,9 +18,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
"github.com/go-kit/kit/log/level"
|
"github.com/go-kit/kit/log/level"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
@@ -33,8 +35,8 @@ const (
|
|||||||
// Topology represents the logical structure of the overlay network.
|
// Topology represents the logical structure of the overlay network.
|
||||||
type Topology struct {
|
type Topology struct {
|
||||||
// key is the private key of the node creating the topology.
|
// key is the private key of the node creating the topology.
|
||||||
key []byte
|
key wgtypes.Key
|
||||||
port uint32
|
port int
|
||||||
// Location is the logical location of the local host.
|
// Location is the logical location of the local host.
|
||||||
location string
|
location string
|
||||||
segments []*segment
|
segments []*segment
|
||||||
@@ -47,7 +49,7 @@ type Topology struct {
|
|||||||
leader bool
|
leader bool
|
||||||
// persistentKeepalive is the interval in seconds of the emission
|
// persistentKeepalive is the interval in seconds of the emission
|
||||||
// of keepalive packets by the local node to its peers.
|
// of keepalive packets by the local node to its peers.
|
||||||
persistentKeepalive int
|
persistentKeepalive time.Duration
|
||||||
// privateIP is the private IP address of the local node.
|
// privateIP is the private IP address of the local node.
|
||||||
privateIP *net.IPNet
|
privateIP *net.IPNet
|
||||||
// subnet is the Pod subnet of the local node.
|
// subnet is the Pod subnet of the local node.
|
||||||
@@ -59,15 +61,16 @@ type Topology struct {
|
|||||||
// is equal to the Kilo subnet.
|
// is equal to the Kilo subnet.
|
||||||
wireGuardCIDR *net.IPNet
|
wireGuardCIDR *net.IPNet
|
||||||
// discoveredEndpoints is the updated map of valid discovered Endpoints
|
// discoveredEndpoints is the updated map of valid discovered Endpoints
|
||||||
discoveredEndpoints map[string]*wireguard.Endpoint
|
discoveredEndpoints map[string]*net.UDPAddr
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// segment represents one logical unit in the topology that is united by one common WireGuard IP.
|
||||||
type segment struct {
|
type segment struct {
|
||||||
allowedIPs []*net.IPNet
|
allowedIPs []net.IPNet
|
||||||
endpoint *wireguard.Endpoint
|
endpoint *wireguard.Endpoint
|
||||||
key []byte
|
key wgtypes.Key
|
||||||
persistentKeepalive int
|
persistentKeepalive time.Duration
|
||||||
// Location is the logical location of this segment.
|
// Location is the logical location of this segment.
|
||||||
location string
|
location string
|
||||||
|
|
||||||
@@ -85,11 +88,11 @@ type segment struct {
|
|||||||
// allowedLocationIPs are not part of the cluster and are not peers.
|
// allowedLocationIPs are not part of the cluster and are not peers.
|
||||||
// They are directly routable from nodes within the segment.
|
// They are directly routable from nodes within the segment.
|
||||||
// A classic example is a printer that ought to be routable from other locations.
|
// A classic example is a printer that ought to be routable from other locations.
|
||||||
allowedLocationIPs []*net.IPNet
|
allowedLocationIPs []net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTopology creates a new Topology struct from a given set of nodes and peers.
|
// NewTopology creates a new Topology struct from a given set of nodes and peers.
|
||||||
func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int, logger log.Logger) (*Topology, error) {
|
func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port int, key wgtypes.Key, subnet *net.IPNet, persistentKeepalive time.Duration, logger log.Logger) (*Topology, error) {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.NewNopLogger()
|
logger = log.NewNopLogger()
|
||||||
}
|
}
|
||||||
@@ -120,7 +123,18 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
localLocation = nodeLocationPrefix + hostname
|
localLocation = nodeLocationPrefix + hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet, wireGuardCIDR: subnet, discoveredEndpoints: make(map[string]*wireguard.Endpoint), logger: logger}
|
t := Topology{
|
||||||
|
key: key,
|
||||||
|
port: port,
|
||||||
|
hostname: hostname,
|
||||||
|
location: localLocation,
|
||||||
|
persistentKeepalive: persistentKeepalive,
|
||||||
|
privateIP: nodes[hostname].InternalIP,
|
||||||
|
subnet: nodes[hostname].Subnet,
|
||||||
|
wireGuardCIDR: subnet,
|
||||||
|
discoveredEndpoints: make(map[string]*net.UDPAddr),
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
for location := range topoMap {
|
for location := range topoMap {
|
||||||
// Sort the location so the result is stable.
|
// Sort the location so the result is stable.
|
||||||
sort.Slice(topoMap[location], func(i, j int) bool {
|
sort.Slice(topoMap[location], func(i, j int) bool {
|
||||||
@@ -130,9 +144,9 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
if location == localLocation && topoMap[location][leader].Name == hostname {
|
if location == localLocation && topoMap[location][leader].Name == hostname {
|
||||||
t.leader = true
|
t.leader = true
|
||||||
}
|
}
|
||||||
var allowedIPs []*net.IPNet
|
var allowedIPs []net.IPNet
|
||||||
allowedLocationIPsMap := make(map[string]struct{})
|
allowedLocationIPsMap := make(map[string]struct{})
|
||||||
var allowedLocationIPs []*net.IPNet
|
var allowedLocationIPs []net.IPNet
|
||||||
var cidrs []*net.IPNet
|
var cidrs []*net.IPNet
|
||||||
var hostnames []string
|
var hostnames []string
|
||||||
var privateIPs []net.IP
|
var privateIPs []net.IP
|
||||||
@@ -142,7 +156,9 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
// - the node's WireGuard IP
|
// - the node's WireGuard IP
|
||||||
// - the node's internal IP
|
// - the node's internal IP
|
||||||
// - IPs that were specified by the allowed-location-ips annotation
|
// - IPs that were specified by the allowed-location-ips annotation
|
||||||
allowedIPs = append(allowedIPs, node.Subnet)
|
if node.Subnet != nil {
|
||||||
|
allowedIPs = append(allowedIPs, *node.Subnet)
|
||||||
|
}
|
||||||
for _, ip := range node.AllowedLocationIPs {
|
for _, ip := range node.AllowedLocationIPs {
|
||||||
if _, ok := allowedLocationIPsMap[ip.String()]; !ok {
|
if _, ok := allowedLocationIPsMap[ip.String()]; !ok {
|
||||||
allowedLocationIPs = append(allowedLocationIPs, ip)
|
allowedLocationIPs = append(allowedLocationIPs, ip)
|
||||||
@@ -150,7 +166,7 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if node.InternalIP != nil {
|
if node.InternalIP != nil {
|
||||||
allowedIPs = append(allowedIPs, oneAddressCIDR(node.InternalIP.IP))
|
allowedIPs = append(allowedIPs, *oneAddressCIDR(node.InternalIP.IP))
|
||||||
privateIPs = append(privateIPs, node.InternalIP.IP)
|
privateIPs = append(privateIPs, node.InternalIP.IP)
|
||||||
}
|
}
|
||||||
cidrs = append(cidrs, node.Subnet)
|
cidrs = append(cidrs, node.Subnet)
|
||||||
@@ -172,6 +188,8 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
privateIPs: privateIPs,
|
privateIPs: privateIPs,
|
||||||
allowedLocationIPs: allowedLocationIPs,
|
allowedLocationIPs: allowedLocationIPs,
|
||||||
})
|
})
|
||||||
|
level.Debug(t.logger).Log("msg", "generated segment", "location", location, "allowedIPs", allowedIPs, "endpoint", topoMap[location][leader].Endpoint, "cidrs", cidrs, "hostnames", hostnames, "leader", leader, "privateIPs", privateIPs, "allowedLocationIPs", allowedLocationIPs)
|
||||||
|
|
||||||
}
|
}
|
||||||
// Sort the Topology segments so the result is stable.
|
// Sort the Topology segments so the result is stable.
|
||||||
sort.Slice(t.segments, func(i, j int) bool {
|
sort.Slice(t.segments, func(i, j int) bool {
|
||||||
@@ -200,7 +218,7 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
return nil, errors.New("failed to allocate an IP address; ran out of IP addresses")
|
return nil, errors.New("failed to allocate an IP address; ran out of IP addresses")
|
||||||
}
|
}
|
||||||
segment.wireGuardIP = ipNet.IP
|
segment.wireGuardIP = ipNet.IP
|
||||||
segment.allowedIPs = append(segment.allowedIPs, oneAddressCIDR(ipNet.IP))
|
segment.allowedIPs = append(segment.allowedIPs, *oneAddressCIDR(ipNet.IP))
|
||||||
if t.leader && segment.location == t.location {
|
if t.leader && segment.location == t.location {
|
||||||
t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
|
t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
|
||||||
}
|
}
|
||||||
@@ -218,14 +236,15 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
segment.allowedLocationIPs = t.filterAllowedLocationIPs(segment.allowedLocationIPs, segment.location)
|
segment.allowedLocationIPs = t.filterAllowedLocationIPs(segment.allowedLocationIPs, segment.location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
level.Debug(t.logger).Log("msg", "generated topology", "location", t.location, "hostname", t.hostname, "wireGuardIP", t.wireGuardCIDR, "privateIP", t.privateIP, "subnet", t.subnet, "leader", t.leader)
|
||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func intersect(n1, n2 *net.IPNet) bool {
|
func intersect(n1, n2 net.IPNet) bool {
|
||||||
return n1.Contains(n2.IP) || n2.Contains(n1.IP)
|
return n1.Contains(n2.IP) || n2.Contains(n1.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topology) filterAllowedLocationIPs(ips []*net.IPNet, location string) (ret []*net.IPNet) {
|
func (t *Topology) filterAllowedLocationIPs(ips []net.IPNet, location string) (ret []net.IPNet) {
|
||||||
CheckIPs:
|
CheckIPs:
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
for _, s := range t.segments {
|
for _, s := range t.segments {
|
||||||
@@ -267,14 +286,14 @@ CheckIPs:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Topology) updateEndpoint(endpoint *wireguard.Endpoint, key []byte, persistentKeepalive int) *wireguard.Endpoint {
|
func (t *Topology) updateEndpoint(endpoint *wireguard.Endpoint, key wgtypes.Key, persistentKeepalive *time.Duration) *wireguard.Endpoint {
|
||||||
// Do not update non-nat peers
|
// Do not update non-nat peers
|
||||||
if persistentKeepalive == 0 {
|
if persistentKeepalive == nil || *persistentKeepalive == time.Duration(0) {
|
||||||
return endpoint
|
return endpoint
|
||||||
}
|
}
|
||||||
e, ok := t.discoveredEndpoints[string(key)]
|
e, ok := t.discoveredEndpoints[key.String()]
|
||||||
if ok {
|
if ok {
|
||||||
return e
|
return wireguard.NewEndpointFromUDPAddr(e)
|
||||||
}
|
}
|
||||||
return endpoint
|
return endpoint
|
||||||
}
|
}
|
||||||
@@ -282,30 +301,37 @@ func (t *Topology) updateEndpoint(endpoint *wireguard.Endpoint, key []byte, pers
|
|||||||
// Conf generates a WireGuard configuration file for a given Topology.
|
// Conf generates a WireGuard configuration file for a given Topology.
|
||||||
func (t *Topology) Conf() *wireguard.Conf {
|
func (t *Topology) Conf() *wireguard.Conf {
|
||||||
c := &wireguard.Conf{
|
c := &wireguard.Conf{
|
||||||
Interface: &wireguard.Interface{
|
Config: wgtypes.Config{
|
||||||
PrivateKey: t.key,
|
PrivateKey: &t.key,
|
||||||
ListenPort: t.port,
|
ListenPort: &t.port,
|
||||||
|
ReplacePeers: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, s := range t.segments {
|
for _, s := range t.segments {
|
||||||
if s.location == t.location {
|
if s.location == t.location {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peer := &wireguard.Peer{
|
peer := wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: append(s.allowedIPs, s.allowedLocationIPs...),
|
AllowedIPs: append(s.allowedIPs, s.allowedLocationIPs...),
|
||||||
Endpoint: t.updateEndpoint(s.endpoint, s.key, s.persistentKeepalive),
|
PersistentKeepaliveInterval: &t.persistentKeepalive,
|
||||||
PersistentKeepalive: t.persistentKeepalive,
|
|
||||||
PublicKey: s.key,
|
PublicKey: s.key,
|
||||||
|
ReplaceAllowedIPs: true,
|
||||||
|
},
|
||||||
|
Endpoint: t.updateEndpoint(s.endpoint, s.key, &s.persistentKeepalive),
|
||||||
}
|
}
|
||||||
c.Peers = append(c.Peers, peer)
|
c.Peers = append(c.Peers, peer)
|
||||||
}
|
}
|
||||||
for _, p := range t.peers {
|
for _, p := range t.peers {
|
||||||
peer := &wireguard.Peer{
|
peer := wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: p.AllowedIPs,
|
AllowedIPs: p.AllowedIPs,
|
||||||
Endpoint: t.updateEndpoint(p.Endpoint, p.PublicKey, p.PersistentKeepalive),
|
PersistentKeepaliveInterval: &t.persistentKeepalive,
|
||||||
PersistentKeepalive: t.persistentKeepalive,
|
|
||||||
PresharedKey: p.PresharedKey,
|
PresharedKey: p.PresharedKey,
|
||||||
PublicKey: p.PublicKey,
|
PublicKey: p.PublicKey,
|
||||||
|
ReplaceAllowedIPs: true,
|
||||||
|
},
|
||||||
|
Endpoint: t.updateEndpoint(p.Endpoint, p.PublicKey, p.PersistentKeepaliveInterval),
|
||||||
}
|
}
|
||||||
c.Peers = append(c.Peers, peer)
|
c.Peers = append(c.Peers, peer)
|
||||||
}
|
}
|
||||||
@@ -319,34 +345,39 @@ func (t *Topology) AsPeer() *wireguard.Peer {
|
|||||||
if s.location != t.location {
|
if s.location != t.location {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return &wireguard.Peer{
|
p := &wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: s.allowedIPs,
|
AllowedIPs: s.allowedIPs,
|
||||||
Endpoint: s.endpoint,
|
|
||||||
PublicKey: s.key,
|
PublicKey: s.key,
|
||||||
|
},
|
||||||
|
Endpoint: s.endpoint,
|
||||||
}
|
}
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerConf generates a WireGuard configuration file for a given peer in a Topology.
|
// PeerConf generates a WireGuard configuration file for a given peer in a Topology.
|
||||||
func (t *Topology) PeerConf(name string) *wireguard.Conf {
|
func (t *Topology) PeerConf(name string) *wireguard.Conf {
|
||||||
var pka int
|
var pka *time.Duration
|
||||||
var psk []byte
|
var psk *wgtypes.Key
|
||||||
for i := range t.peers {
|
for i := range t.peers {
|
||||||
if t.peers[i].Name == name {
|
if t.peers[i].Name == name {
|
||||||
pka = t.peers[i].PersistentKeepalive
|
pka = t.peers[i].PersistentKeepaliveInterval
|
||||||
psk = t.peers[i].PresharedKey
|
psk = t.peers[i].PresharedKey
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := &wireguard.Conf{}
|
c := &wireguard.Conf{}
|
||||||
for _, s := range t.segments {
|
for _, s := range t.segments {
|
||||||
peer := &wireguard.Peer{
|
peer := wireguard.Peer{
|
||||||
AllowedIPs: s.allowedIPs,
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
Endpoint: s.endpoint,
|
AllowedIPs: append(s.allowedIPs, s.allowedLocationIPs...),
|
||||||
PersistentKeepalive: pka,
|
PersistentKeepaliveInterval: pka,
|
||||||
PresharedKey: psk,
|
PresharedKey: psk,
|
||||||
PublicKey: s.key,
|
PublicKey: s.key,
|
||||||
|
},
|
||||||
|
Endpoint: t.updateEndpoint(s.endpoint, s.key, &s.persistentKeepalive),
|
||||||
}
|
}
|
||||||
c.Peers = append(c.Peers, peer)
|
c.Peers = append(c.Peers, peer)
|
||||||
}
|
}
|
||||||
@@ -354,11 +385,13 @@ func (t *Topology) PeerConf(name string) *wireguard.Conf {
|
|||||||
if t.peers[i].Name == name {
|
if t.peers[i].Name == name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peer := &wireguard.Peer{
|
peer := wireguard.Peer{
|
||||||
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: t.peers[i].AllowedIPs,
|
AllowedIPs: t.peers[i].AllowedIPs,
|
||||||
PersistentKeepalive: pka,
|
PersistentKeepaliveInterval: pka,
|
||||||
PublicKey: t.peers[i].PublicKey,
|
PublicKey: t.peers[i].PublicKey,
|
||||||
Endpoint: t.peers[i].Endpoint,
|
},
|
||||||
|
Endpoint: t.updateEndpoint(t.peers[i].Endpoint, t.peers[i].PublicKey, t.peers[i].PersistentKeepaliveInterval),
|
||||||
}
|
}
|
||||||
c.Peers = append(c.Peers, peer)
|
c.Peers = append(c.Peers, peer)
|
||||||
}
|
}
|
||||||
@@ -379,13 +412,13 @@ func findLeader(nodes []*Node) int {
|
|||||||
var leaders, public []int
|
var leaders, public []int
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
if nodes[i].Leader {
|
if nodes[i].Leader {
|
||||||
if isPublic(nodes[i].Endpoint.IP) {
|
if isPublic(nodes[i].Endpoint.IP()) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
leaders = append(leaders, i)
|
leaders = append(leaders, i)
|
||||||
|
|
||||||
}
|
}
|
||||||
if isPublic(nodes[i].Endpoint.IP) {
|
if nodes[i].Endpoint.IP() != nil && isPublic(nodes[i].Endpoint.IP()) {
|
||||||
public = append(public, i)
|
public = append(public, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,11 +438,13 @@ func deduplicatePeerIPs(peers []*Peer) []*Peer {
|
|||||||
p := Peer{
|
p := Peer{
|
||||||
Name: peer.Name,
|
Name: peer.Name,
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
Endpoint: peer.Endpoint,
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
PersistentKeepalive: peer.PersistentKeepalive,
|
PersistentKeepaliveInterval: peer.PersistentKeepaliveInterval,
|
||||||
PresharedKey: peer.PresharedKey,
|
PresharedKey: peer.PresharedKey,
|
||||||
PublicKey: peer.PublicKey,
|
PublicKey: peer.PublicKey,
|
||||||
},
|
},
|
||||||
|
Endpoint: peer.Endpoint,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, ip := range peer.AllowedIPs {
|
for _, ip := range peer.AllowedIPs {
|
||||||
if _, ok := ips[ip.String()]; ok {
|
if _, ok := ips[ip.String()]; ok {
|
||||||
|
@@ -18,9 +18,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log"
|
"github.com/go-kit/kit/log"
|
||||||
"github.com/kylelemons/godebug/pretty"
|
"github.com/kylelemons/godebug/pretty"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
@@ -29,17 +31,25 @@ func allowedIPs(ips ...string) string {
|
|||||||
return strings.Join(ips, ", ")
|
return strings.Join(ips, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustParseCIDR(s string) (r *net.IPNet) {
|
func mustParseCIDR(s string) (r net.IPNet) {
|
||||||
if _, ip, err := net.ParseCIDR(s); err != nil {
|
if _, ip, err := net.ParseCIDR(s); err != nil {
|
||||||
panic("failed to parse CIDR")
|
panic("failed to parse CIDR")
|
||||||
} else {
|
} else {
|
||||||
r = ip
|
r = *ip
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
var (
|
||||||
key := []byte("private")
|
key1 = wgtypes.Key{'k', 'e', 'y', '1'}
|
||||||
|
key2 = wgtypes.Key{'k', 'e', 'y', '2'}
|
||||||
|
key3 = wgtypes.Key{'k', 'e', 'y', '3'}
|
||||||
|
key4 = wgtypes.Key{'k', 'e', 'y', '4'}
|
||||||
|
key5 = wgtypes.Key{'k', 'e', 'y', '5'}
|
||||||
|
)
|
||||||
|
|
||||||
|
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, wgtypes.Key, int) {
|
||||||
|
key := wgtypes.Key{'p', 'r', 'i', 'v'}
|
||||||
e1 := &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(16, 32)}
|
e1 := &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(16, 32)}
|
||||||
e2 := &net.IPNet{IP: net.ParseIP("10.1.0.2").To4(), Mask: net.CIDRMask(16, 32)}
|
e2 := &net.IPNet{IP: net.ParseIP("10.1.0.2").To4(), Mask: net.CIDRMask(16, 32)}
|
||||||
e3 := &net.IPNet{IP: net.ParseIP("10.1.0.3").To4(), Mask: net.CIDRMask(16, 32)}
|
e3 := &net.IPNet{IP: net.ParseIP("10.1.0.3").To4(), Mask: net.CIDRMask(16, 32)}
|
||||||
@@ -50,62 +60,63 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
|||||||
nodes := map[string]*Node{
|
nodes := map[string]*Node{
|
||||||
"a": {
|
"a": {
|
||||||
Name: "a",
|
Name: "a",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e1.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e1.IP, DefaultKiloPort),
|
||||||
InternalIP: i1,
|
InternalIP: i1,
|
||||||
Location: "1",
|
Location: "1",
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
Key: []byte("key1"),
|
Key: key1,
|
||||||
PersistentKeepalive: 25,
|
PersistentKeepalive: 25,
|
||||||
},
|
},
|
||||||
"b": {
|
"b": {
|
||||||
Name: "b",
|
Name: "b",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e2.IP, DefaultKiloPort),
|
||||||
InternalIP: i1,
|
InternalIP: i1,
|
||||||
Location: "2",
|
Location: "2",
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
Key: []byte("key2"),
|
Key: key2,
|
||||||
AllowedLocationIPs: []*net.IPNet{i3},
|
AllowedLocationIPs: []net.IPNet{*i3},
|
||||||
},
|
},
|
||||||
"c": {
|
"c": {
|
||||||
Name: "c",
|
Name: "c",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e3.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e3.IP, DefaultKiloPort),
|
||||||
InternalIP: i2,
|
InternalIP: i2,
|
||||||
// Same location as node b.
|
// Same location as node b.
|
||||||
Location: "2",
|
Location: "2",
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.3.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.3.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
Key: []byte("key3"),
|
Key: key3,
|
||||||
},
|
},
|
||||||
"d": {
|
"d": {
|
||||||
Name: "d",
|
Name: "d",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e4.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e4.IP, DefaultKiloPort),
|
||||||
// Same location as node a, but without private IP
|
// Same location as node a, but without private IP
|
||||||
Location: "1",
|
Location: "1",
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.4.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.4.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
Key: []byte("key4"),
|
Key: key4,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
peers := map[string]*Peer{
|
peers := map[string]*Peer{
|
||||||
"a": {
|
"a": {
|
||||||
Name: "a",
|
Name: "a",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
AllowedIPs: []*net.IPNet{
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.5.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.5.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.5.0.2"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.5.0.2"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
PublicKey: []byte("key4"),
|
PublicKey: key4,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"b": {
|
"b": {
|
||||||
Name: "b",
|
Name: "b",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
AllowedIPs: []*net.IPNet{
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.5.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.5.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
Endpoint: &wireguard.Endpoint{
|
PublicKey: key5,
|
||||||
DNSOrIP: wireguard.DNSOrIP{IP: net.ParseIP("192.168.0.1")},
|
|
||||||
Port: DefaultKiloPort,
|
|
||||||
},
|
},
|
||||||
PublicKey: []byte("key5"),
|
Endpoint: wireguard.NewEndpoint(net.ParseIP("192.168.0.1"), DefaultKiloPort),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -138,7 +149,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -149,7 +160,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, *nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -161,7 +172,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -189,7 +200,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -200,7 +211,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, *nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -212,7 +223,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -240,7 +251,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: DefaultKiloSubnet,
|
wireGuardCIDR: DefaultKiloSubnet,
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -251,7 +262,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, *nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -263,7 +274,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -291,7 +302,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -302,7 +313,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -314,7 +325,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
@@ -325,7 +336,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w3,
|
wireGuardIP: w3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -353,7 +364,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -364,7 +375,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -376,7 +387,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
@@ -387,7 +398,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w3,
|
wireGuardIP: w3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -415,7 +426,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w3, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w3, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -426,7 +437,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -438,7 +449,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
@@ -449,7 +460,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w3,
|
wireGuardIP: w3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -477,7 +488,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardCIDR: &net.IPNet{IP: w4, Mask: net.CIDRMask(16, 32)},
|
wireGuardCIDR: &net.IPNet{IP: w4, Mask: net.CIDRMask(16, 32)},
|
||||||
segments: []*segment{
|
segments: []*segment{
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
@@ -488,7 +499,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w1,
|
wireGuardIP: w1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
@@ -500,7 +511,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
@@ -511,7 +522,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
wireGuardIP: w3,
|
wireGuardIP: w3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
@@ -539,7 +550,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) *Topology {
|
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port int, key wgtypes.Key, subnet *net.IPNet, persistentKeepalive time.Duration) *Topology {
|
||||||
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive, nil)
|
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to generate Topology: %v", err)
|
t.Errorf("failed to generate Topology: %v", err)
|
||||||
@@ -547,211 +558,6 @@ func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, gran
|
|||||||
return topo
|
return topo
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConf(t *testing.T) {
|
|
||||||
nodes, peers, key, port := setup(t)
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
topology *Topology
|
|
||||||
result string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "logical from a",
|
|
||||||
topology: mustTopo(t, nodes, peers, LogicalGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key2
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32, 192.168.178.3/32
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.3/32
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "logical from b",
|
|
||||||
topology: mustTopo(t, nodes, peers, LogicalGranularity, nodes["b"].Name, port, key, DefaultKiloSubnet, nodes["b"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key1
|
|
||||||
Endpoint = 10.1.0.1:51820
|
|
||||||
AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.3/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "logical from c",
|
|
||||||
topology: mustTopo(t, nodes, peers, LogicalGranularity, nodes["c"].Name, port, key, DefaultKiloSubnet, nodes["c"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key1
|
|
||||||
Endpoint = 10.1.0.1:51820
|
|
||||||
AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.3/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "full from a",
|
|
||||||
topology: mustTopo(t, nodes, peers, FullGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key2
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key3
|
|
||||||
Endpoint = 10.1.0.3:51820
|
|
||||||
AllowedIPs = 10.2.3.0/24, 192.168.0.2/32, 10.4.0.3/32
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.4/32
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
PersistentKeepalive = 25
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "full from b",
|
|
||||||
topology: mustTopo(t, nodes, peers, FullGranularity, nodes["b"].Name, port, key, DefaultKiloSubnet, nodes["b"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key1
|
|
||||||
Endpoint = 10.1.0.1:51820
|
|
||||||
AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key3
|
|
||||||
Endpoint = 10.1.0.3:51820
|
|
||||||
AllowedIPs = 10.2.3.0/24, 192.168.0.2/32, 10.4.0.3/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.4/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "full from c",
|
|
||||||
topology: mustTopo(t, nodes, peers, FullGranularity, nodes["c"].Name, port, key, DefaultKiloSubnet, nodes["c"].PersistentKeepalive),
|
|
||||||
result: `[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key1
|
|
||||||
Endpoint = 10.1.0.1:51820
|
|
||||||
AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key2
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
Endpoint = 10.1.0.4:51820
|
|
||||||
AllowedIPs = 10.2.4.0/24, 10.4.0.4/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key4
|
|
||||||
AllowedIPs = 10.5.0.1/24, 10.5.0.2/24
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key5
|
|
||||||
Endpoint = 192.168.0.1:51820
|
|
||||||
AllowedIPs = 10.5.0.3/24
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
conf := tc.topology.Conf()
|
|
||||||
if !conf.Equal(wireguard.Parse([]byte(tc.result))) {
|
|
||||||
buf, err := conf.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("test case %q: failed to render conf: %v", tc.name, err)
|
|
||||||
}
|
|
||||||
t.Errorf("test case %q: expected %s got %s", tc.name, tc.result, string(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindLeader(t *testing.T) {
|
func TestFindLeader(t *testing.T) {
|
||||||
ip, e1, err := net.ParseCIDR("10.0.0.1/32")
|
ip, e1, err := net.ParseCIDR("10.0.0.1/32")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -767,24 +573,24 @@ func TestFindLeader(t *testing.T) {
|
|||||||
nodes := []*Node{
|
nodes := []*Node{
|
||||||
{
|
{
|
||||||
Name: "a",
|
Name: "a",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e1.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e1.IP, DefaultKiloPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "b",
|
Name: "b",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e2.IP, DefaultKiloPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "c",
|
Name: "c",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e2.IP, DefaultKiloPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "d",
|
Name: "d",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e1.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e1.IP, DefaultKiloPort),
|
||||||
Leader: true,
|
Leader: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
Endpoint: wireguard.NewEndpoint(e2.IP, DefaultKiloPort),
|
||||||
Leader: true,
|
Leader: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -840,44 +646,53 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
p1 := &Peer{
|
p1 := &Peer{
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key1"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
|
||||||
|
PublicKey: key1,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
p2 := &Peer{
|
p2 := &Peer{
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key2"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key2,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
p3 := &Peer{
|
p3 := &Peer{
|
||||||
Name: "3",
|
Name: "3",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key3"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key3,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
p4 := &Peer{
|
p4 := &Peer{
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key4"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key4,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
@@ -898,14 +713,16 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key2"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key2,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "simple dupe reversed",
|
name: "simple dupe reversed",
|
||||||
peers: []*Peer{p2, p1},
|
peers: []*Peer{p2, p1},
|
||||||
@@ -914,14 +731,16 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key1"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key1,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "one duplicates all",
|
name: "one duplicates all",
|
||||||
peers: []*Peer{p3, p2, p1, p4},
|
peers: []*Peer{p3, p2, p1, p4},
|
||||||
@@ -930,19 +749,25 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key2"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PublicKey: key2,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key1"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PublicKey: key1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key4"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
|
PublicKey: key4,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -954,17 +779,20 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Name: "4",
|
Name: "4",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key4"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key4,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.3"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Peer: wireguard.Peer{
|
Peer: wireguard.Peer{
|
||||||
PublicKey: []byte("key1"),
|
PeerConfig: wgtypes.PeerConfig{
|
||||||
AllowedIPs: []*net.IPNet{
|
PublicKey: key1,
|
||||||
|
AllowedIPs: []net.IPNet{
|
||||||
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.1"), Mask: net.CIDRMask(24, 32)},
|
||||||
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
|
||||||
},
|
},
|
||||||
@@ -972,6 +800,7 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
out := deduplicatePeerIPs(tc.peers)
|
out := deduplicatePeerIPs(tc.peers)
|
||||||
if diff := pretty.Compare(out, tc.out); diff != "" {
|
if diff := pretty.Compare(out, tc.out); diff != "" {
|
||||||
@@ -985,12 +814,12 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
topo := mustTopo(t, nodes, peers, LogicalGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive)
|
topo := mustTopo(t, nodes, peers, LogicalGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive)
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
allowedLocationIPs map[int][]*net.IPNet
|
allowedLocationIPs map[int][]net.IPNet
|
||||||
result map[int][]*net.IPNet
|
result map[int][]net.IPNet
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "nothing to filter",
|
name: "nothing to filter",
|
||||||
allowedLocationIPs: map[int][]*net.IPNet{
|
allowedLocationIPs: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.4/32"),
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
},
|
},
|
||||||
@@ -1002,7 +831,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
mustParseCIDR("192.168.178.7/32"),
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: map[int][]*net.IPNet{
|
result: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.4/32"),
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
},
|
},
|
||||||
@@ -1017,7 +846,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "intersections between segments",
|
name: "intersections between segments",
|
||||||
allowedLocationIPs: map[int][]*net.IPNet{
|
allowedLocationIPs: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.4/32"),
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
mustParseCIDR("192.168.178.8/32"),
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
@@ -1031,7 +860,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
mustParseCIDR("192.168.178.4/32"),
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: map[int][]*net.IPNet{
|
result: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.8/32"),
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
},
|
},
|
||||||
@@ -1047,7 +876,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "intersections with wireGuardCIDR",
|
name: "intersections with wireGuardCIDR",
|
||||||
allowedLocationIPs: map[int][]*net.IPNet{
|
allowedLocationIPs: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("10.4.0.1/32"),
|
mustParseCIDR("10.4.0.1/32"),
|
||||||
mustParseCIDR("192.168.178.8/32"),
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
@@ -1060,7 +889,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
mustParseCIDR("192.168.178.7/32"),
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: map[int][]*net.IPNet{
|
result: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.8/32"),
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
},
|
},
|
||||||
@@ -1075,7 +904,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "intersections with more than one allowedLocationIPs",
|
name: "intersections with more than one allowedLocationIPs",
|
||||||
allowedLocationIPs: map[int][]*net.IPNet{
|
allowedLocationIPs: map[int][]net.IPNet{
|
||||||
0: {
|
0: {
|
||||||
mustParseCIDR("192.168.178.8/32"),
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
},
|
},
|
||||||
@@ -1086,7 +915,7 @@ func TestFilterAllowedIPs(t *testing.T) {
|
|||||||
mustParseCIDR("192.168.178.7/24"),
|
mustParseCIDR("192.168.178.7/24"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: map[int][]*net.IPNet{
|
result: map[int][]net.IPNet{
|
||||||
0: {},
|
0: {},
|
||||||
1: {},
|
1: {},
|
||||||
2: {
|
2: {
|
||||||
|
@@ -15,16 +15,15 @@
|
|||||||
package wireguard
|
package wireguard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,10 +31,6 @@ type section string
|
|||||||
type key string
|
type key string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
separator = "="
|
|
||||||
dumpSeparator = "\t"
|
|
||||||
dumpNone = "(none)"
|
|
||||||
dumpOff = "off"
|
|
||||||
interfaceSection section = "Interface"
|
interfaceSection section = "Interface"
|
||||||
peerSection section = "Peer"
|
peerSection section = "Peer"
|
||||||
listenPortKey key = "ListenPort"
|
listenPortKey key = "ListenPort"
|
||||||
@@ -47,56 +42,209 @@ const (
|
|||||||
publicKeyKey key = "PublicKey"
|
publicKeyKey key = "PublicKey"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dumpInterfaceIndex int
|
|
||||||
|
|
||||||
const (
|
|
||||||
dumpInterfacePrivateKeyIndex = iota
|
|
||||||
dumpInterfacePublicKeyIndex
|
|
||||||
dumpInterfaceListenPortIndex
|
|
||||||
dumpInterfaceFWMarkIndex
|
|
||||||
dumpInterfaceLen
|
|
||||||
)
|
|
||||||
|
|
||||||
type dumpPeerIndex int
|
|
||||||
|
|
||||||
const (
|
|
||||||
dumpPeerPublicKeyIndex = iota
|
|
||||||
dumpPeerPresharedKeyIndex
|
|
||||||
dumpPeerEndpointIndex
|
|
||||||
dumpPeerAllowedIPsIndex
|
|
||||||
dumpPeerLatestHandshakeIndex
|
|
||||||
dumpPeerTransferRXIndex
|
|
||||||
dumpPeerTransferTXIndex
|
|
||||||
dumpPeerPersistentKeepaliveIndex
|
|
||||||
dumpPeerLen
|
|
||||||
)
|
|
||||||
|
|
||||||
// Conf represents a WireGuard configuration file.
|
// Conf represents a WireGuard configuration file.
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
Interface *Interface
|
wgtypes.Config
|
||||||
Peers []*Peer
|
// The Peers field is shadowed because every Peer needs the Endpoint field that contains a DNS endpoint.
|
||||||
|
Peers []Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface represents the `interface` section of a WireGuard configuration.
|
// WGConfig returns a wgytpes.Config from a Conf.
|
||||||
type Interface struct {
|
func (c *Conf) WGConfig() wgtypes.Config {
|
||||||
ListenPort uint32
|
if c == nil {
|
||||||
PrivateKey []byte
|
// The empty Config will do nothing, when applied.
|
||||||
|
return wgtypes.Config{}
|
||||||
|
}
|
||||||
|
r := c.Config
|
||||||
|
wgPs := make([]wgtypes.PeerConfig, len(c.Peers))
|
||||||
|
for i, p := range c.Peers {
|
||||||
|
wgPs[i] = p.PeerConfig
|
||||||
|
if p.Endpoint.Resolved() {
|
||||||
|
// We can ingore the error because we already checked if the Endpoint was resolved in the above line.
|
||||||
|
wgPs[i].Endpoint, _ = p.Endpoint.UDPAddr(false)
|
||||||
|
}
|
||||||
|
wgPs[i].ReplaceAllowedIPs = true
|
||||||
|
}
|
||||||
|
r.Peers = wgPs
|
||||||
|
r.ReplacePeers = true
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint represents a WireGuard endpoint.
|
||||||
|
type Endpoint struct {
|
||||||
|
udpAddr *net.UDPAddr
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEndpoint returns an Endpoint from a string.
|
||||||
|
// The input should look like "10.0.0.0:100", "[ff10::10]:100"
|
||||||
|
// or "example.com:100".
|
||||||
|
func ParseEndpoint(endpoint string) *Endpoint {
|
||||||
|
if len(endpoint) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hostRaw, portRaw, err := net.SplitHostPort(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
port, err := strconv.ParseUint(portRaw, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(validation.IsValidPortNum(int(port))) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(hostRaw)
|
||||||
|
if ip == nil {
|
||||||
|
if len(validation.IsDNS1123Subdomain(hostRaw)) == 0 {
|
||||||
|
return &Endpoint{
|
||||||
|
addr: endpoint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// ResolveUDPAddr will not resolve the endpoint as long as a valid IP and port is given.
|
||||||
|
// This should be the case here.
|
||||||
|
u, err := net.ResolveUDPAddr("udp", endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
u.IP = cutIP(u.IP)
|
||||||
|
return &Endpoint{
|
||||||
|
udpAddr: u,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEndpointFromUDPAddr returns an Endpoint from a net.UDPAddr.
|
||||||
|
func NewEndpointFromUDPAddr(u *net.UDPAddr) *Endpoint {
|
||||||
|
if u != nil {
|
||||||
|
u.IP = cutIP(u.IP)
|
||||||
|
}
|
||||||
|
return &Endpoint{
|
||||||
|
udpAddr: u,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEndpoint returns an Endpoint from a net.IP and port.
|
||||||
|
func NewEndpoint(ip net.IP, port int) *Endpoint {
|
||||||
|
return &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: cutIP(ip),
|
||||||
|
Port: port,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ready return true, if the Enpoint is ready.
|
||||||
|
// Ready means that an IP or DN and port exists.
|
||||||
|
func (e *Endpoint) Ready() bool {
|
||||||
|
if e == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (e.udpAddr != nil && e.udpAddr.IP != nil && e.udpAddr.Port > 0) || len(e.addr) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Port returns the port of the Endpoint.
|
||||||
|
func (e *Endpoint) Port() int {
|
||||||
|
if !e.Ready() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if e.udpAddr != nil {
|
||||||
|
return e.udpAddr.Port
|
||||||
|
}
|
||||||
|
// We can ignore the errors here bacause the returned port will be "".
|
||||||
|
// This will result to Port 0 after the conversion to and int.
|
||||||
|
_, p, _ := net.SplitHostPort(e.addr)
|
||||||
|
port, _ := strconv.ParseUint(p, 10, 32)
|
||||||
|
return int(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasDNS returns true if the endpoint has a DN.
|
||||||
|
func (e *Endpoint) HasDNS() bool {
|
||||||
|
return e != nil && e.addr != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNS returns the DN of the Endpoint.
|
||||||
|
func (e *Endpoint) DNS() string {
|
||||||
|
if e == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
_, s, _ := net.SplitHostPort(e.addr)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolved returns true, if the DN of the Endpoint was resolved
|
||||||
|
// or if the Endpoint has a resolved endpoint.
|
||||||
|
func (e *Endpoint) Resolved() bool {
|
||||||
|
return e != nil && e.udpAddr != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDPAddr returns the UDPAddr of the Endpoint. If resolve is false,
|
||||||
|
// UDPAddr() will not try to resolve a DN name, if the Endpoint is not yet resolved.
|
||||||
|
func (e *Endpoint) UDPAddr(resolve bool) (*net.UDPAddr, error) {
|
||||||
|
if !e.Ready() {
|
||||||
|
return nil, errors.New("Enpoint is not ready")
|
||||||
|
}
|
||||||
|
if e.udpAddr != nil {
|
||||||
|
// Make a copy of the UDPAddr to protect it from modification outside this package.
|
||||||
|
h := *e.udpAddr
|
||||||
|
return &h, nil
|
||||||
|
}
|
||||||
|
if !resolve {
|
||||||
|
return nil, errors.New("Endpoint is not resolved")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if e.udpAddr, err = net.ResolveUDPAddr("udp", e.addr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Make a copy of the UDPAddr to protect it from modification outside this package.
|
||||||
|
h := *e.udpAddr
|
||||||
|
return &h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP returns the IP address of the Enpoint or nil.
|
||||||
|
func (e *Endpoint) IP() net.IP {
|
||||||
|
if !e.Resolved() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.udpAddr.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// String will return the endpoint as a string.
|
||||||
|
// If a DN exists, it will take prcedence over the resolved endpoint.
|
||||||
|
func (e *Endpoint) String() string {
|
||||||
|
return e.StringOpt(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringOpt will return the string of the Endpoint.
|
||||||
|
// If dnsFirst is false, the resolved Endpoint will
|
||||||
|
// take precedence over the DN.
|
||||||
|
func (e *Endpoint) StringOpt(dnsFirst bool) string {
|
||||||
|
if e == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if e.udpAddr != nil && (!dnsFirst || e.addr == "") {
|
||||||
|
return e.udpAddr.String()
|
||||||
|
}
|
||||||
|
return e.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal will return true, if the Enpoints are equal.
|
||||||
|
// If dnsFirst is false, the DN will only be compared if
|
||||||
|
// the IPs are nil.
|
||||||
|
func (e *Endpoint) Equal(b *Endpoint, dnsFirst bool) bool {
|
||||||
|
return e.StringOpt(dnsFirst) == b.StringOpt(dnsFirst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer represents a `peer` section of a WireGuard configuration.
|
// Peer represents a `peer` section of a WireGuard configuration.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
AllowedIPs []*net.IPNet
|
wgtypes.PeerConfig
|
||||||
Endpoint *Endpoint
|
Endpoint *Endpoint
|
||||||
PersistentKeepalive int
|
|
||||||
PresharedKey []byte
|
|
||||||
PublicKey []byte
|
|
||||||
// The following fields are part of the runtime information, not the configuration.
|
|
||||||
LatestHandshake time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeduplicateIPs eliminates duplicate allowed IPs.
|
// DeduplicateIPs eliminates duplicate allowed IPs.
|
||||||
func (p *Peer) DeduplicateIPs() {
|
func (p *Peer) DeduplicateIPs() {
|
||||||
var ips []*net.IPNet
|
var ips []net.IPNet
|
||||||
seen := make(map[string]struct{})
|
seen := make(map[string]struct{})
|
||||||
for _, ip := range p.AllowedIPs {
|
for _, ip := range p.AllowedIPs {
|
||||||
if _, ok := seen[ip.String()]; ok {
|
if _, ok := seen[ip.String()]; ok {
|
||||||
@@ -108,181 +256,27 @@ func (p *Peer) DeduplicateIPs() {
|
|||||||
p.AllowedIPs = ips
|
p.AllowedIPs = ips
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint represents an `endpoint` key of a `peer` section.
|
|
||||||
type Endpoint struct {
|
|
||||||
DNSOrIP
|
|
||||||
Port uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// String prints the string representation of the endpoint.
|
|
||||||
func (e *Endpoint) String() string {
|
|
||||||
if e == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
dnsOrIP := e.DNSOrIP.String()
|
|
||||||
if e.IP != nil && len(e.IP) == net.IPv6len {
|
|
||||||
dnsOrIP = "[" + dnsOrIP + "]"
|
|
||||||
}
|
|
||||||
return dnsOrIP + ":" + strconv.FormatUint(uint64(e.Port), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal compares two endpoints.
|
|
||||||
func (e *Endpoint) Equal(b *Endpoint, DNSFirst bool) bool {
|
|
||||||
if (e == nil) != (b == nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if e != nil {
|
|
||||||
if e.Port != b.Port {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if DNSFirst {
|
|
||||||
// Check the DNS name first if it was resolved.
|
|
||||||
if e.DNS != b.DNS {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if e.DNS == "" && !e.IP.Equal(b.IP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// IPs take priority, so check them first.
|
|
||||||
if !e.IP.Equal(b.IP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Only check the DNS name if the IP is empty.
|
|
||||||
if e.IP == nil && e.DNS != b.DNS {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSOrIP represents either a DNS name or an IP address.
|
|
||||||
// IPs, as they are more specific, are preferred.
|
|
||||||
type DNSOrIP struct {
|
|
||||||
DNS string
|
|
||||||
IP net.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// String prints the string representation of the struct.
|
|
||||||
func (d DNSOrIP) String() string {
|
|
||||||
if d.IP != nil {
|
|
||||||
return d.IP.String()
|
|
||||||
}
|
|
||||||
return d.DNS
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses a given WireGuard configuration file and produces a Conf struct.
|
|
||||||
func Parse(buf []byte) *Conf {
|
|
||||||
var (
|
|
||||||
active section
|
|
||||||
kv []string
|
|
||||||
c Conf
|
|
||||||
err error
|
|
||||||
iface *Interface
|
|
||||||
i int
|
|
||||||
k key
|
|
||||||
line, v string
|
|
||||||
peer *Peer
|
|
||||||
port uint64
|
|
||||||
)
|
|
||||||
s := bufio.NewScanner(bytes.NewBuffer(buf))
|
|
||||||
for s.Scan() {
|
|
||||||
line = strings.TrimSpace(s.Text())
|
|
||||||
// Skip comments.
|
|
||||||
if strings.HasPrefix(line, "#") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Line is a section title.
|
|
||||||
if strings.HasPrefix(line, "[") {
|
|
||||||
if peer != nil {
|
|
||||||
c.Peers = append(c.Peers, peer)
|
|
||||||
peer = nil
|
|
||||||
}
|
|
||||||
if iface != nil {
|
|
||||||
c.Interface = iface
|
|
||||||
iface = nil
|
|
||||||
}
|
|
||||||
active = section(strings.TrimSpace(strings.Trim(line, "[]")))
|
|
||||||
switch active {
|
|
||||||
case interfaceSection:
|
|
||||||
iface = new(Interface)
|
|
||||||
case peerSection:
|
|
||||||
peer = new(Peer)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
kv = strings.SplitN(line, separator, 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
k = key(strings.TrimSpace(kv[0]))
|
|
||||||
v = strings.TrimSpace(kv[1])
|
|
||||||
switch active {
|
|
||||||
case interfaceSection:
|
|
||||||
switch k {
|
|
||||||
case listenPortKey:
|
|
||||||
port, err = strconv.ParseUint(v, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iface.ListenPort = uint32(port)
|
|
||||||
case privateKeyKey:
|
|
||||||
iface.PrivateKey = []byte(v)
|
|
||||||
}
|
|
||||||
case peerSection:
|
|
||||||
switch k {
|
|
||||||
case allowedIPsKey:
|
|
||||||
err = peer.parseAllowedIPs(v)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case endpointKey:
|
|
||||||
err = peer.parseEndpoint(v)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case persistentKeepaliveKey:
|
|
||||||
i, err = strconv.Atoi(v)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
peer.PersistentKeepalive = i
|
|
||||||
case presharedKeyKey:
|
|
||||||
peer.PresharedKey = []byte(v)
|
|
||||||
case publicKeyKey:
|
|
||||||
peer.PublicKey = []byte(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if peer != nil {
|
|
||||||
c.Peers = append(c.Peers, peer)
|
|
||||||
}
|
|
||||||
if iface != nil {
|
|
||||||
c.Interface = iface
|
|
||||||
}
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes renders a WireGuard configuration to bytes.
|
// Bytes renders a WireGuard configuration to bytes.
|
||||||
func (c *Conf) Bytes() ([]byte, error) {
|
func (c *Conf) Bytes() ([]byte, error) {
|
||||||
|
if c == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 512))
|
buf := bytes.NewBuffer(make([]byte, 0, 512))
|
||||||
if c.Interface != nil {
|
if c.PrivateKey != nil {
|
||||||
if err = writeSection(buf, interfaceSection); err != nil {
|
if err = writeSection(buf, interfaceSection); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write interface: %v", err)
|
return nil, fmt.Errorf("failed to write interface: %v", err)
|
||||||
}
|
}
|
||||||
if err = writePKey(buf, privateKeyKey, c.Interface.PrivateKey); err != nil {
|
if err = writePKey(buf, privateKeyKey, c.PrivateKey); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write private key: %v", err)
|
return nil, fmt.Errorf("failed to write private key: %v", err)
|
||||||
}
|
}
|
||||||
if err = writeValue(buf, listenPortKey, strconv.FormatUint(uint64(c.Interface.ListenPort), 10)); err != nil {
|
if err = writeValue(buf, listenPortKey, strconv.Itoa(*c.ListenPort)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write listen port: %v", err)
|
return nil, fmt.Errorf("failed to write listen port: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, p := range c.Peers {
|
for i, p := range c.Peers {
|
||||||
// Add newlines to make the formatting nicer.
|
// Add newlines to make the formatting nicer.
|
||||||
if i == 0 && c.Interface != nil || i != 0 {
|
if i == 0 && c.PrivateKey != nil || i != 0 {
|
||||||
if err = buf.WriteByte('\n'); err != nil {
|
if err = buf.WriteByte('\n'); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -297,71 +291,103 @@ func (c *Conf) Bytes() ([]byte, error) {
|
|||||||
if err = writeEndpoint(buf, p.Endpoint); err != nil {
|
if err = writeEndpoint(buf, p.Endpoint); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write endpoint: %v", err)
|
return nil, fmt.Errorf("failed to write endpoint: %v", err)
|
||||||
}
|
}
|
||||||
if err = writeValue(buf, persistentKeepaliveKey, strconv.Itoa(p.PersistentKeepalive)); err != nil {
|
if p.PersistentKeepaliveInterval == nil {
|
||||||
|
p.PersistentKeepaliveInterval = new(time.Duration)
|
||||||
|
}
|
||||||
|
if err = writeValue(buf, persistentKeepaliveKey, strconv.FormatUint(uint64(*p.PersistentKeepaliveInterval/time.Second), 10)); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write persistent keepalive: %v", err)
|
return nil, fmt.Errorf("failed to write persistent keepalive: %v", err)
|
||||||
}
|
}
|
||||||
if err = writePKey(buf, presharedKeyKey, p.PresharedKey); err != nil {
|
if err = writePKey(buf, presharedKeyKey, p.PresharedKey); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write preshared key: %v", err)
|
return nil, fmt.Errorf("failed to write preshared key: %v", err)
|
||||||
}
|
}
|
||||||
if err = writePKey(buf, publicKeyKey, p.PublicKey); err != nil {
|
if err = writePKey(buf, publicKeyKey, &p.PublicKey); err != nil {
|
||||||
return nil, fmt.Errorf("failed to write public key: %v", err)
|
return nil, fmt.Errorf("failed to write public key: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal checks if two WireGuard configurations are equivalent.
|
// Equal returns true if the Conf and wgtypes.Device are equal.
|
||||||
func (c *Conf) Equal(b *Conf) bool {
|
func (c *Conf) Equal(d *wgtypes.Device) (bool, string) {
|
||||||
if (c.Interface == nil) != (b.Interface == nil) {
|
if c == nil || d == nil {
|
||||||
return false
|
return c == nil && d == nil, "nil values"
|
||||||
}
|
}
|
||||||
if c.Interface != nil {
|
if c.ListenPort == nil || *c.ListenPort != d.ListenPort {
|
||||||
if c.Interface.ListenPort != b.Interface.ListenPort || !bytes.Equal(c.Interface.PrivateKey, b.Interface.PrivateKey) {
|
return false, fmt.Sprintf("port: old=%q, new=\"%v\"", d.ListenPort, c.ListenPort)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
if c.PrivateKey == nil || *c.PrivateKey != d.PrivateKey {
|
||||||
|
return false, fmt.Sprintf("private key: old=\"%s...\", new=\"%s\"", d.PrivateKey.String()[0:5], c.PrivateKey.String()[0:5])
|
||||||
}
|
}
|
||||||
if len(c.Peers) != len(b.Peers) {
|
if len(c.Peers) != len(d.Peers) {
|
||||||
return false
|
return false, fmt.Sprintf("number of peers: old=%d, new=%d", len(d.Peers), len(c.Peers))
|
||||||
}
|
}
|
||||||
|
sortPeerConfigs(d.Peers)
|
||||||
sortPeers(c.Peers)
|
sortPeers(c.Peers)
|
||||||
sortPeers(b.Peers)
|
|
||||||
for i := range c.Peers {
|
for i := range c.Peers {
|
||||||
if len(c.Peers[i].AllowedIPs) != len(b.Peers[i].AllowedIPs) {
|
if len(c.Peers[i].AllowedIPs) != len(d.Peers[i].AllowedIPs) {
|
||||||
return false
|
return false, fmt.Sprintf("Peer %d allowed IP length: old=%d, new=%d", i, len(d.Peers[i].AllowedIPs), len(c.Peers[i].AllowedIPs))
|
||||||
}
|
}
|
||||||
sortCIDRs(c.Peers[i].AllowedIPs)
|
sortCIDRs(c.Peers[i].AllowedIPs)
|
||||||
sortCIDRs(b.Peers[i].AllowedIPs)
|
sortCIDRs(d.Peers[i].AllowedIPs)
|
||||||
for j := range c.Peers[i].AllowedIPs {
|
for j := range c.Peers[i].AllowedIPs {
|
||||||
if c.Peers[i].AllowedIPs[j].String() != b.Peers[i].AllowedIPs[j].String() {
|
if c.Peers[i].AllowedIPs[j].String() != d.Peers[i].AllowedIPs[j].String() {
|
||||||
return false
|
return false, fmt.Sprintf("Peer %d allowed IP: old=%q, new=%q", i, d.Peers[i].AllowedIPs[j].String(), c.Peers[i].AllowedIPs[j].String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !c.Peers[i].Endpoint.Equal(b.Peers[i].Endpoint, false) {
|
if c.Peers[i].Endpoint == nil || d.Peers[i].Endpoint == nil {
|
||||||
return false
|
return c.Peers[i].Endpoint == nil && d.Peers[i].Endpoint == nil, "peer endpoints: nil value"
|
||||||
}
|
}
|
||||||
if c.Peers[i].PersistentKeepalive != b.Peers[i].PersistentKeepalive || !bytes.Equal(c.Peers[i].PresharedKey, b.Peers[i].PresharedKey) || !bytes.Equal(c.Peers[i].PublicKey, b.Peers[i].PublicKey) {
|
if c.Peers[i].Endpoint.StringOpt(false) != d.Peers[i].Endpoint.String() {
|
||||||
return false
|
return false, fmt.Sprintf("Peer %d endpoint: old=%q, new=%q", i, d.Peers[i].Endpoint.String(), c.Peers[i].Endpoint.StringOpt(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
pki := time.Duration(0)
|
||||||
|
if p := c.Peers[i].PersistentKeepaliveInterval; p != nil {
|
||||||
|
pki = *p
|
||||||
|
}
|
||||||
|
psk := wgtypes.Key{}
|
||||||
|
if p := c.Peers[i].PresharedKey; p != nil {
|
||||||
|
psk = *p
|
||||||
|
}
|
||||||
|
if pki != d.Peers[i].PersistentKeepaliveInterval || psk != d.Peers[i].PresharedKey || c.Peers[i].PublicKey != d.Peers[i].PublicKey {
|
||||||
|
return false, "persistent keepalive or pershared key"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortPeers(peers []*Peer) {
|
func sortPeerConfigs(peers []wgtypes.Peer) {
|
||||||
sort.Slice(peers, func(i, j int) bool {
|
sort.Slice(peers, func(i, j int) bool {
|
||||||
if bytes.Compare(peers[i].PublicKey, peers[j].PublicKey) < 0 {
|
if peers[i].PublicKey.String() < peers[j].PublicKey.String() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortCIDRs(cidrs []*net.IPNet) {
|
func sortPeers(peers []Peer) {
|
||||||
|
sort.Slice(peers, func(i, j int) bool {
|
||||||
|
if peers[i].PublicKey.String() < peers[j].PublicKey.String() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortCIDRs(cidrs []net.IPNet) {
|
||||||
sort.Slice(cidrs, func(i, j int) bool {
|
sort.Slice(cidrs, func(i, j int) bool {
|
||||||
return cidrs[i].String() < cidrs[j].String()
|
return cidrs[i].String() < cidrs[j].String()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAllowedIPs(buf *bytes.Buffer, ais []*net.IPNet) error {
|
func cutIP(ip net.IP) net.IP {
|
||||||
|
if i4 := ip.To4(); i4 != nil {
|
||||||
|
return i4
|
||||||
|
}
|
||||||
|
return ip.To16()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAllowedIPs(buf *bytes.Buffer, ais []net.IPNet) error {
|
||||||
if len(ais) == 0 {
|
if len(ais) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -382,15 +408,16 @@ func writeAllowedIPs(buf *bytes.Buffer, ais []*net.IPNet) error {
|
|||||||
return buf.WriteByte('\n')
|
return buf.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePKey(buf *bytes.Buffer, k key, b []byte) error {
|
func writePKey(buf *bytes.Buffer, k key, b *wgtypes.Key) error {
|
||||||
if len(b) == 0 {
|
// Print nothing if the public key was never initialized.
|
||||||
|
if b == nil || (wgtypes.Key{}) == *b {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if err = writeKey(buf, k); err != nil {
|
if err = writeKey(buf, k); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err = buf.Write(b); err != nil {
|
if _, err = buf.Write([]byte(b.String())); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return buf.WriteByte('\n')
|
return buf.WriteByte('\n')
|
||||||
@@ -408,14 +435,15 @@ func writeValue(buf *bytes.Buffer, k key, v string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeEndpoint(buf *bytes.Buffer, e *Endpoint) error {
|
func writeEndpoint(buf *bytes.Buffer, e *Endpoint) error {
|
||||||
if e == nil {
|
str := e.String()
|
||||||
|
if str == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if err = writeKey(buf, endpointKey); err != nil {
|
if err = writeKey(buf, endpointKey); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err = buf.WriteString(e.String()); err != nil {
|
if _, err = buf.WriteString(str); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return buf.WriteByte('\n')
|
return buf.WriteByte('\n')
|
||||||
@@ -443,177 +471,3 @@ func writeKey(buf *bytes.Buffer, k key) error {
|
|||||||
_, err = buf.WriteString(" = ")
|
_, err = buf.WriteString(" = ")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
errParseEndpoint = errors.New("could not parse Endpoint")
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Peer) parseEndpoint(v string) error {
|
|
||||||
var (
|
|
||||||
kv []string
|
|
||||||
err error
|
|
||||||
ip, ip4 net.IP
|
|
||||||
port uint64
|
|
||||||
)
|
|
||||||
kv = strings.Split(v, ":")
|
|
||||||
if len(kv) < 2 {
|
|
||||||
return errParseEndpoint
|
|
||||||
}
|
|
||||||
port, err = strconv.ParseUint(kv[len(kv)-1], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d := DNSOrIP{}
|
|
||||||
ip = net.ParseIP(strings.Trim(strings.Join(kv[:len(kv)-1], ":"), "[]"))
|
|
||||||
if ip == nil {
|
|
||||||
if len(validation.IsDNS1123Subdomain(kv[0])) != 0 {
|
|
||||||
return errParseEndpoint
|
|
||||||
}
|
|
||||||
d.DNS = kv[0]
|
|
||||||
} else {
|
|
||||||
if ip4 = ip.To4(); ip4 != nil {
|
|
||||||
d.IP = ip4
|
|
||||||
} else {
|
|
||||||
d.IP = ip.To16()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Endpoint = &Endpoint{
|
|
||||||
DNSOrIP: d,
|
|
||||||
Port: uint32(port),
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) parseAllowedIPs(v string) error {
|
|
||||||
var (
|
|
||||||
ai *net.IPNet
|
|
||||||
kv []string
|
|
||||||
err error
|
|
||||||
i int
|
|
||||||
ip, ip4 net.IP
|
|
||||||
)
|
|
||||||
|
|
||||||
kv = strings.Split(v, ",")
|
|
||||||
for i = range kv {
|
|
||||||
ip, ai, err = net.ParseCIDR(strings.TrimSpace(kv[i]))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ip4 = ip.To4(); ip4 != nil {
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
ip = ip.To16()
|
|
||||||
}
|
|
||||||
ai.IP = ip
|
|
||||||
p.AllowedIPs = append(p.AllowedIPs, ai)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseDump parses a given WireGuard dump and produces a Conf struct.
|
|
||||||
func ParseDump(buf []byte) (*Conf, error) {
|
|
||||||
// from man wg, show section:
|
|
||||||
// If dump is specified, then several lines are printed;
|
|
||||||
// the first contains in order separated by tab: private-key, public-key, listen-port, fw‐mark.
|
|
||||||
// Subsequent lines are printed for each peer and contain in order separated by tab:
|
|
||||||
// public-key, preshared-key, endpoint, allowed-ips, latest-handshake, transfer-rx, transfer-tx, persistent-keepalive.
|
|
||||||
var (
|
|
||||||
active section
|
|
||||||
values []string
|
|
||||||
c Conf
|
|
||||||
err error
|
|
||||||
iface *Interface
|
|
||||||
peer *Peer
|
|
||||||
port uint64
|
|
||||||
sec int64
|
|
||||||
pka int
|
|
||||||
line int
|
|
||||||
)
|
|
||||||
// First line is Interface
|
|
||||||
active = interfaceSection
|
|
||||||
s := bufio.NewScanner(bytes.NewBuffer(buf))
|
|
||||||
for s.Scan() {
|
|
||||||
values = strings.Split(s.Text(), dumpSeparator)
|
|
||||||
|
|
||||||
switch active {
|
|
||||||
case interfaceSection:
|
|
||||||
if len(values) < dumpInterfaceLen {
|
|
||||||
return nil, fmt.Errorf("invalid interface line: missing fields (%d < %d)", len(values), dumpInterfaceLen)
|
|
||||||
}
|
|
||||||
iface = new(Interface)
|
|
||||||
for i := range values {
|
|
||||||
switch i {
|
|
||||||
case dumpInterfacePrivateKeyIndex:
|
|
||||||
iface.PrivateKey = []byte(values[i])
|
|
||||||
case dumpInterfaceListenPortIndex:
|
|
||||||
port, err = strconv.ParseUint(values[i], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid interface line: error parsing listen-port: %w", err)
|
|
||||||
}
|
|
||||||
iface.ListenPort = uint32(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Interface = iface
|
|
||||||
// Next lines are Peers
|
|
||||||
active = peerSection
|
|
||||||
case peerSection:
|
|
||||||
if len(values) < dumpPeerLen {
|
|
||||||
return nil, fmt.Errorf("invalid peer line %d: missing fields (%d < %d)", line, len(values), dumpPeerLen)
|
|
||||||
}
|
|
||||||
peer = new(Peer)
|
|
||||||
|
|
||||||
for i := range values {
|
|
||||||
switch i {
|
|
||||||
case dumpPeerPublicKeyIndex:
|
|
||||||
peer.PublicKey = []byte(values[i])
|
|
||||||
case dumpPeerPresharedKeyIndex:
|
|
||||||
if values[i] == dumpNone {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
peer.PresharedKey = []byte(values[i])
|
|
||||||
case dumpPeerEndpointIndex:
|
|
||||||
if values[i] == dumpNone {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = peer.parseEndpoint(values[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid peer line %d: error parsing endpoint: %w", line, err)
|
|
||||||
}
|
|
||||||
case dumpPeerAllowedIPsIndex:
|
|
||||||
if values[i] == dumpNone {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = peer.parseAllowedIPs(values[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid peer line %d: error parsing allowed-ips: %w", line, err)
|
|
||||||
}
|
|
||||||
case dumpPeerLatestHandshakeIndex:
|
|
||||||
if values[i] == "0" {
|
|
||||||
// Use go zero value, not unix 0 timestamp.
|
|
||||||
peer.LatestHandshake = time.Time{}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sec, err = strconv.ParseInt(values[i], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid peer line %d: error parsing latest-handshake: %w", line, err)
|
|
||||||
}
|
|
||||||
peer.LatestHandshake = time.Unix(sec, 0)
|
|
||||||
case dumpPeerPersistentKeepaliveIndex:
|
|
||||||
if values[i] == dumpOff {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pka, err = strconv.Atoi(values[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid peer line %d: error parsing persistent-keepalive: %w", line, err)
|
|
||||||
}
|
|
||||||
peer.PersistentKeepalive = pka
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Peers = append(c.Peers, peer)
|
|
||||||
peer = nil
|
|
||||||
}
|
|
||||||
line++
|
|
||||||
}
|
|
||||||
return &c, nil
|
|
||||||
}
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 the Kilo authors
|
// Copyright 2021 the Kilo authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -21,336 +21,431 @@ import (
|
|||||||
"github.com/kylelemons/godebug/pretty"
|
"github.com/kylelemons/godebug/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompareConf(t *testing.T) {
|
func TestNewEndpoint(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
a []byte
|
ip net.IP
|
||||||
b []byte
|
port int
|
||||||
out bool
|
out *Endpoint
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "no ip, no port",
|
||||||
a: []byte{},
|
out: &Endpoint{
|
||||||
b: []byte{},
|
udpAddr: &net.UDPAddr{},
|
||||||
out: true,
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "key and value order",
|
name: "only port",
|
||||||
a: []byte(`[Interface]
|
ip: nil,
|
||||||
PrivateKey = private
|
port: 99,
|
||||||
ListenPort = 51820
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
[Peer]
|
Port: 99,
|
||||||
Endpoint = 10.1.0.2:51820
|
},
|
||||||
PresharedKey = psk
|
},
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(`[Interface]
|
|
||||||
ListenPort = 51820
|
|
||||||
PrivateKey = private
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32, 10.2.2.0/24
|
|
||||||
PresharedKey = psk
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
`),
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "whitespace",
|
name: "only ipv4",
|
||||||
a: []byte(`[Interface]
|
ip: net.ParseIP("10.0.0.0"),
|
||||||
PrivateKey = private
|
out: &Endpoint{
|
||||||
ListenPort = 51820
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0").To4(),
|
||||||
[Peer]
|
},
|
||||||
Endpoint = 10.1.0.2:51820
|
},
|
||||||
PresharedKey = psk
|
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(`[Interface]
|
|
||||||
PrivateKey=private
|
|
||||||
ListenPort=51820
|
|
||||||
[Peer]
|
|
||||||
Endpoint=10.1.0.2:51820
|
|
||||||
PresharedKey = psk
|
|
||||||
PublicKey=key
|
|
||||||
AllowedIPs=10.2.2.0/24,192.168.0.1/32,10.2.3.0/24,192.168.0.2/32,10.4.0.2/32
|
|
||||||
`),
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing key",
|
name: "only ipv6",
|
||||||
a: []byte(`[Interface]
|
ip: net.ParseIP("ff50::10"),
|
||||||
PrivateKey = private
|
out: &Endpoint{
|
||||||
ListenPort = 51820
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff50::10").To16(),
|
||||||
[Peer]
|
},
|
||||||
Endpoint = 10.1.0.2:51820
|
},
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(`[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
out: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "different value",
|
name: "ipv4",
|
||||||
a: []byte(`[Interface]
|
ip: net.ParseIP("10.0.0.0"),
|
||||||
PrivateKey = private
|
port: 1000,
|
||||||
ListenPort = 51820
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
[Peer]
|
IP: net.ParseIP("10.0.0.0").To4(),
|
||||||
Endpoint = 10.1.0.2:51820
|
Port: 1000,
|
||||||
PublicKey = key
|
},
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
},
|
||||||
`),
|
|
||||||
b: []byte(`[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PublicKey = key2
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
out: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "section order",
|
name: "ipv6",
|
||||||
a: []byte(`[Interface]
|
ip: net.ParseIP("ff50::10"),
|
||||||
PrivateKey = private
|
port: 1000,
|
||||||
ListenPort = 51820
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
[Peer]
|
IP: net.ParseIP("ff50::10").To16(),
|
||||||
Endpoint = 10.1.0.2:51820
|
Port: 1000,
|
||||||
PresharedKey = psk
|
},
|
||||||
PublicKey = key
|
},
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(`[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PresharedKey = psk
|
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
`),
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "out of order peers",
|
name: "ipv6",
|
||||||
a: []byte(`[Interface]
|
ip: net.ParseIP("fc00:f853:ccd:e793::3"),
|
||||||
PrivateKey = private
|
port: 51820,
|
||||||
ListenPort = 51820
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
[Peer]
|
IP: net.ParseIP("fc00:f853:ccd:e793::3").To16(),
|
||||||
Endpoint = 10.1.0.2:51820
|
Port: 51820,
|
||||||
PresharedKey = psk2
|
},
|
||||||
PublicKey = key2
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PresharedKey = psk1
|
|
||||||
PublicKey = key1
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(`[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PresharedKey = psk1
|
|
||||||
PublicKey = key1
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PresharedKey = psk2
|
|
||||||
PublicKey = key2
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "one empty",
|
|
||||||
a: []byte(`[Interface]
|
|
||||||
PrivateKey = private
|
|
||||||
ListenPort = 51820
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
Endpoint = 10.1.0.2:51820
|
|
||||||
PresharedKey = psk
|
|
||||||
PublicKey = key
|
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
|
||||||
`),
|
|
||||||
b: []byte(``),
|
|
||||||
out: false,
|
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
equal := Parse(tc.a).Equal(Parse(tc.b))
|
out := NewEndpoint(tc.ip, tc.port)
|
||||||
if equal != tc.out {
|
if diff := pretty.Compare(out, tc.out); diff != "" {
|
||||||
t.Errorf("test case %q: expected %t, got %t", tc.name, tc.out, equal)
|
t.Errorf("%d %s: got diff:\n%s\n", i, tc.name, diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompareEndpoint(t *testing.T) {
|
func TestParseEndpoint(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
|
name string
|
||||||
|
str string
|
||||||
|
out *Endpoint
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no ip, no port",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only port",
|
||||||
|
str: ":1000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv4",
|
||||||
|
str: "10.0.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv6",
|
||||||
|
str: "ff50::10",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv4",
|
||||||
|
str: "10.0.0.0:1000",
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0").To4(),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6",
|
||||||
|
str: "[ff50::10]:1000",
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff50::10").To16(),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
out := ParseEndpoint(tc.str)
|
||||||
|
if diff := pretty.Compare(out, tc.out); diff != "" {
|
||||||
|
t.Errorf("ParseEndpoint %s(%d): got diff:\n%s\n", tc.name, i, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewEndpointFromUDPAddr(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
name string
|
||||||
|
u *net.UDPAddr
|
||||||
|
out *Endpoint
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no ip, no port",
|
||||||
|
out: &Endpoint{
|
||||||
|
addr: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only port",
|
||||||
|
u: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
addr: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv4",
|
||||||
|
u: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0").To4(),
|
||||||
|
},
|
||||||
|
addr: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv6",
|
||||||
|
u: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff60::10"),
|
||||||
|
},
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff60::10").To16(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv4",
|
||||||
|
u: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0").To4(),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6",
|
||||||
|
u: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff50::10"),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
out: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff50::10").To16(),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
out := NewEndpointFromUDPAddr(tc.u)
|
||||||
|
if diff := pretty.Compare(out, tc.out); diff != "" {
|
||||||
|
t.Errorf("ParseEndpoint %s(%d): got diff:\n%s\n", tc.name, i, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReady(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
name string
|
||||||
|
in *Endpoint
|
||||||
|
r bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil",
|
||||||
|
r: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no ip, no port",
|
||||||
|
in: &Endpoint{
|
||||||
|
addr: "",
|
||||||
|
udpAddr: &net.UDPAddr{},
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only port",
|
||||||
|
in: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv4",
|
||||||
|
in: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only ipv6",
|
||||||
|
in: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff60::10"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv4",
|
||||||
|
in: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ipv6",
|
||||||
|
in: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
IP: net.ParseIP("ff50::10"),
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if tc.r != tc.in.Ready() {
|
||||||
|
t.Errorf("Endpoint.Ready() %s(%d): expected=%v\tgot=%v\n", tc.name, i, tc.r, tc.in.Ready())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqual(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
a *Endpoint
|
a *Endpoint
|
||||||
b *Endpoint
|
b *Endpoint
|
||||||
dnsFirst bool
|
df bool
|
||||||
out bool
|
r bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "both nil",
|
name: "nil dns last",
|
||||||
a: nil,
|
r: true,
|
||||||
b: nil,
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "a nil",
|
name: "nil dns first",
|
||||||
a: nil,
|
df: true,
|
||||||
b: &Endpoint{},
|
r: true,
|
||||||
out: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "b nil",
|
name: "equal: only port",
|
||||||
a: &Endpoint{},
|
a: &Endpoint{
|
||||||
b: nil,
|
udpAddr: &net.UDPAddr{
|
||||||
out: false,
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "zero",
|
name: "not equal: only port",
|
||||||
a: &Endpoint{},
|
a: &Endpoint{
|
||||||
b: &Endpoint{},
|
udpAddr: &net.UDPAddr{
|
||||||
out: true,
|
Port: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1001,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "diff port",
|
name: "equal dns first",
|
||||||
a: &Endpoint{Port: 1234},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 5678},
|
udpAddr: &net.UDPAddr{
|
||||||
out: false,
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "example.com:1000",
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "example.com:1000",
|
||||||
|
},
|
||||||
|
r: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same IP",
|
name: "equal dns last",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
udpAddr: &net.UDPAddr{
|
||||||
out: true,
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "example.com:1000",
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "foo",
|
||||||
|
},
|
||||||
|
r: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "diff IP",
|
name: "unequal dns first",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2")}},
|
udpAddr: &net.UDPAddr{
|
||||||
out: false,
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "example.com:1000",
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "foo",
|
||||||
|
},
|
||||||
|
df: true,
|
||||||
|
r: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same IP ignore DNS",
|
name: "unequal dns last",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "a"}},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "b"}},
|
udpAddr: &net.UDPAddr{
|
||||||
out: true,
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("10.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "foo",
|
||||||
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
udpAddr: &net.UDPAddr{
|
||||||
|
Port: 1000,
|
||||||
|
IP: net.ParseIP("11.0.0.0"),
|
||||||
|
},
|
||||||
|
addr: "foo",
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no IP check DNS",
|
name: "unequal dns last empty IP",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "b"}},
|
addr: "foo",
|
||||||
out: false,
|
},
|
||||||
|
b: &Endpoint{
|
||||||
|
addr: "bar",
|
||||||
|
},
|
||||||
|
r: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no IP check DNS (same)",
|
name: "equal dns last empty IP",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
a: &Endpoint{
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
addr: "foo",
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
b: &Endpoint{
|
||||||
name: "DNS first, ignore IP",
|
addr: "foo",
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "a"}},
|
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2"), DNS: "a"}},
|
|
||||||
dnsFirst: true,
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
{
|
r: true,
|
||||||
name: "DNS first",
|
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "b"}},
|
|
||||||
dnsFirst: true,
|
|
||||||
out: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DNS first, no DNS compare IP",
|
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2"), DNS: ""}},
|
|
||||||
dnsFirst: true,
|
|
||||||
out: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "DNS first, no DNS compare IP (same)",
|
|
||||||
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
|
||||||
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
|
||||||
dnsFirst: true,
|
|
||||||
out: true,
|
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
equal := tc.a.Equal(tc.b, tc.dnsFirst)
|
if out := tc.a.Equal(tc.b, tc.df); out != tc.r {
|
||||||
if equal != tc.out {
|
t.Errorf("ParseEndpoint %s(%d): expected: %v\tgot: %v\n", tc.name, i, tc.r, out)
|
||||||
t.Errorf("test case %q: expected %t, got %t", tc.name, tc.out, equal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareDumpConf(t *testing.T) {
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
d []byte
|
|
||||||
c []byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
d: []byte{},
|
|
||||||
c: []byte{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "redacted copy from wg output",
|
|
||||||
d: []byte(`private B7qk8EMlob0nfado0ABM6HulUV607r4yqtBKjhap7S4= 51820 off
|
|
||||||
key1 (none) 10.254.1.1:51820 100.64.1.0/24,192.168.0.125/32,10.4.0.1/32 1619012801 67048 34952 10
|
|
||||||
key2 (none) 10.254.2.1:51820 100.64.4.0/24,10.69.76.55/32,100.64.3.0/24,10.66.25.131/32,10.4.0.2/32 1619013058 1134456 10077852 10`),
|
|
||||||
c: []byte(`[Interface]
|
|
||||||
ListenPort = 51820
|
|
||||||
PrivateKey = private
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key1
|
|
||||||
AllowedIPs = 100.64.1.0/24, 192.168.0.125/32, 10.4.0.1/32
|
|
||||||
Endpoint = 10.254.1.1:51820
|
|
||||||
PersistentKeepalive = 10
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = key2
|
|
||||||
AllowedIPs = 100.64.4.0/24, 10.69.76.55/32, 100.64.3.0/24, 10.66.25.131/32, 10.4.0.2/32
|
|
||||||
Endpoint = 10.254.2.1:51820
|
|
||||||
PersistentKeepalive = 10`),
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
|
|
||||||
dumpConf, _ := ParseDump(tc.d)
|
|
||||||
conf := Parse(tc.c)
|
|
||||||
// Equal will ignore runtime fields and only compare configuration fields.
|
|
||||||
if !dumpConf.Equal(conf) {
|
|
||||||
diff := pretty.Compare(dumpConf, conf)
|
|
||||||
t.Errorf("test case %q: got diff: %v", tc.name, diff)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,14 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
// +build linux
|
// +build linux
|
||||||
|
|
||||||
package wireguard
|
package wireguard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
@@ -64,74 +63,3 @@ func New(name string, mtu uint) (int, bool, error) {
|
|||||||
}
|
}
|
||||||
return link.Attrs().Index, true, nil
|
return link.Attrs().Index, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys generates a WireGuard private and public key-pair.
|
|
||||||
func Keys() ([]byte, []byte, error) {
|
|
||||||
private, err := GenKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to generate private key: %v", err)
|
|
||||||
}
|
|
||||||
public, err := PubKey(private)
|
|
||||||
return private, public, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenKey generates a WireGuard private key.
|
|
||||||
func GenKey() ([]byte, error) {
|
|
||||||
key, err := exec.Command("wg", "genkey").Output()
|
|
||||||
return bytes.Trim(key, "\n"), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PubKey generates a WireGuard public key for a given private key.
|
|
||||||
func PubKey(key []byte) ([]byte, error) {
|
|
||||||
cmd := exec.Command("wg", "pubkey")
|
|
||||||
stdin, err := cmd.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open pipe to stdin: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer stdin.Close()
|
|
||||||
stdin.Write(key)
|
|
||||||
}()
|
|
||||||
|
|
||||||
public, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate public key: %v", err)
|
|
||||||
}
|
|
||||||
return bytes.Trim(public, "\n"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetConf applies a WireGuard configuration file to the given interface.
|
|
||||||
func SetConf(iface string, path string) error {
|
|
||||||
cmd := exec.Command("wg", "setconf", iface, path)
|
|
||||||
var stderr bytes.Buffer
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("failed to apply the WireGuard configuration: %s", stderr.String())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowConf gets the WireGuard configuration for the given interface.
|
|
||||||
func ShowConf(iface string) ([]byte, error) {
|
|
||||||
cmd := exec.Command("wg", "showconf", iface)
|
|
||||||
var stderr, stdout bytes.Buffer
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read the WireGuard configuration: %s", stderr.String())
|
|
||||||
}
|
|
||||||
return stdout.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowDump gets the WireGuard configuration and runtime information for the given interface.
|
|
||||||
func ShowDump(iface string) ([]byte, error) {
|
|
||||||
cmd := exec.Command("wg", "show", iface, "dump")
|
|
||||||
var stderr, stdout bytes.Buffer
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read the WireGuard dump output: %s", stderr.String())
|
|
||||||
}
|
|
||||||
return stdout.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
1
tools.go
1
tools.go
@@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build tools
|
||||||
// +build tools
|
// +build tools
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
3
vendor/github.com/campoy/embedmd/go.mod
generated
vendored
3
vendor/github.com/campoy/embedmd/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
module github.com/campoy/embedmd
|
|
||||||
|
|
||||||
require github.com/pmezard/go-difflib v1.0.0
|
|
2
vendor/github.com/campoy/embedmd/go.sum
generated
vendored
2
vendor/github.com/campoy/embedmd/go.sum
generated
vendored
@@ -1,2 +0,0 @@
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
module github.com/cespare/xxhash/v2
|
|
||||||
|
|
||||||
go 1.11
|
|
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
8
vendor/github.com/fatih/color/go.mod
generated
vendored
8
vendor/github.com/fatih/color/go.mod
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
module github.com/fatih/color
|
|
||||||
|
|
||||||
go 1.13
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/mattn/go-colorable v0.1.8
|
|
||||||
github.com/mattn/go-isatty v0.0.12
|
|
||||||
)
|
|
7
vendor/github.com/fatih/color/go.sum
generated
vendored
7
vendor/github.com/fatih/color/go.sum
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
5
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
5
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
@@ -1,4 +1 @@
|
|||||||
_testdata/
|
.vscode/
|
||||||
_testdata2/
|
|
||||||
logfmt-fuzz.zip
|
|
||||||
logfmt.test.exe
|
|
||||||
|
2
vendor/github.com/go-logfmt/logfmt/.travis.yml
generated
vendored
2
vendor/github.com/go-logfmt/logfmt/.travis.yml
generated
vendored
@@ -6,6 +6,8 @@ go:
|
|||||||
- "1.9.x"
|
- "1.9.x"
|
||||||
- "1.10.x"
|
- "1.10.x"
|
||||||
- "1.11.x"
|
- "1.11.x"
|
||||||
|
- "1.12.x"
|
||||||
|
- "1.13.x"
|
||||||
- "tip"
|
- "tip"
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
7
vendor/github.com/go-logfmt/logfmt/CHANGELOG.md
generated
vendored
7
vendor/github.com/go-logfmt/logfmt/CHANGELOG.md
generated
vendored
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.5.0] - 2020-01-03
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Remove the dependency on github.com/kr/logfmt by [@ChrisHines]
|
||||||
|
- Move fuzz code to github.com/go-logfmt/fuzzlogfmt by [@ChrisHines]
|
||||||
|
|
||||||
## [0.4.0] - 2018-11-21
|
## [0.4.0] - 2018-11-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -30,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Decoder by [@ChrisHines]
|
- Decoder by [@ChrisHines]
|
||||||
- MarshalKeyvals by [@ChrisHines]
|
- MarshalKeyvals by [@ChrisHines]
|
||||||
|
|
||||||
|
[0.5.0]: https://github.com/go-logfmt/logfmt/compare/v0.4.0...v0.5.0
|
||||||
[0.4.0]: https://github.com/go-logfmt/logfmt/compare/v0.3.0...v0.4.0
|
[0.4.0]: https://github.com/go-logfmt/logfmt/compare/v0.3.0...v0.4.0
|
||||||
[0.3.0]: https://github.com/go-logfmt/logfmt/compare/v0.2.0...v0.3.0
|
[0.3.0]: https://github.com/go-logfmt/logfmt/compare/v0.2.0...v0.3.0
|
||||||
[0.2.0]: https://github.com/go-logfmt/logfmt/compare/v0.1.0...v0.2.0
|
[0.2.0]: https://github.com/go-logfmt/logfmt/compare/v0.1.0...v0.2.0
|
||||||
|
6
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
6
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
@@ -79,7 +79,7 @@ key:
|
|||||||
dec.pos += p
|
dec.pos += p
|
||||||
if dec.pos > start {
|
if dec.pos > start {
|
||||||
dec.key = line[start:dec.pos]
|
dec.key = line[start:dec.pos]
|
||||||
if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
|
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||||
dec.syntaxError(invalidKeyError)
|
dec.syntaxError(invalidKeyError)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ key:
|
|||||||
dec.pos += p
|
dec.pos += p
|
||||||
if dec.pos > start {
|
if dec.pos > start {
|
||||||
dec.key = line[start:dec.pos]
|
dec.key = line[start:dec.pos]
|
||||||
if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
|
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||||
dec.syntaxError(invalidKeyError)
|
dec.syntaxError(invalidKeyError)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ key:
|
|||||||
dec.pos = len(line)
|
dec.pos = len(line)
|
||||||
if dec.pos > start {
|
if dec.pos > start {
|
||||||
dec.key = line[start:dec.pos]
|
dec.key = line[start:dec.pos]
|
||||||
if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
|
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||||
dec.syntaxError(invalidKeyError)
|
dec.syntaxError(invalidKeyError)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
126
vendor/github.com/go-logfmt/logfmt/fuzz.go
generated
vendored
126
vendor/github.com/go-logfmt/logfmt/fuzz.go
generated
vendored
@@ -1,126 +0,0 @@
|
|||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package logfmt
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
kr "github.com/kr/logfmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fuzz checks reserialized data matches
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
parsed, err := parse(data)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var w1 bytes.Buffer
|
|
||||||
if err = write(parsed, &w1); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
parsed, err = parse(w1.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
var w2 bytes.Buffer
|
|
||||||
if err = write(parsed, &w2); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if !bytes.Equal(w1.Bytes(), w2.Bytes()) {
|
|
||||||
panic(fmt.Sprintf("reserialized data does not match:\n%q\n%q\n", w1.Bytes(), w2.Bytes()))
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuzzVsKR checks go-logfmt/logfmt against kr/logfmt
|
|
||||||
func FuzzVsKR(data []byte) int {
|
|
||||||
parsed, err := parse(data)
|
|
||||||
parsedKR, errKR := parseKR(data)
|
|
||||||
|
|
||||||
// github.com/go-logfmt/logfmt is a stricter parser. It returns errors for
|
|
||||||
// more inputs than github.com/kr/logfmt. Ignore any inputs that have a
|
|
||||||
// stict error.
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fail if the more forgiving parser finds an error not found by the
|
|
||||||
// stricter parser.
|
|
||||||
if errKR != nil {
|
|
||||||
panic(fmt.Sprintf("unmatched error: %v", errKR))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(parsed, parsedKR) {
|
|
||||||
panic(fmt.Sprintf("parsers disagree:\n%+v\n%+v\n", parsed, parsedKR))
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
type kv struct {
|
|
||||||
k, v []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse(data []byte) ([][]kv, error) {
|
|
||||||
var got [][]kv
|
|
||||||
dec := NewDecoder(bytes.NewReader(data))
|
|
||||||
for dec.ScanRecord() {
|
|
||||||
var kvs []kv
|
|
||||||
for dec.ScanKeyval() {
|
|
||||||
kvs = append(kvs, kv{dec.Key(), dec.Value()})
|
|
||||||
}
|
|
||||||
got = append(got, kvs)
|
|
||||||
}
|
|
||||||
return got, dec.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseKR(data []byte) ([][]kv, error) {
|
|
||||||
var (
|
|
||||||
s = bufio.NewScanner(bytes.NewReader(data))
|
|
||||||
err error
|
|
||||||
h saveHandler
|
|
||||||
got [][]kv
|
|
||||||
)
|
|
||||||
for err == nil && s.Scan() {
|
|
||||||
h.kvs = nil
|
|
||||||
err = kr.Unmarshal(s.Bytes(), &h)
|
|
||||||
got = append(got, h.kvs)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
err = s.Err()
|
|
||||||
}
|
|
||||||
return got, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type saveHandler struct {
|
|
||||||
kvs []kv
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *saveHandler) HandleLogfmt(key, val []byte) error {
|
|
||||||
if len(key) == 0 {
|
|
||||||
key = nil
|
|
||||||
}
|
|
||||||
if len(val) == 0 {
|
|
||||||
val = nil
|
|
||||||
}
|
|
||||||
h.kvs = append(h.kvs, kv{key, val})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func write(recs [][]kv, w io.Writer) error {
|
|
||||||
enc := NewEncoder(w)
|
|
||||||
for _, rec := range recs {
|
|
||||||
for _, f := range rec {
|
|
||||||
if err := enc.EncodeKeyval(f.k, f.v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := enc.EndRecord(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
3
vendor/github.com/go-logfmt/logfmt/go.mod
generated
vendored
3
vendor/github.com/go-logfmt/logfmt/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
module github.com/go-logfmt/logfmt
|
|
||||||
|
|
||||||
require github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
|
|
2
vendor/github.com/go-logfmt/logfmt/go.sum
generated
vendored
2
vendor/github.com/go-logfmt/logfmt/go.sum
generated
vendored
@@ -1,2 +0,0 @@
|
|||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
3
vendor/github.com/go-logr/logr/go.mod
generated
vendored
3
vendor/github.com/go-logr/logr/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
module github.com/go-logr/logr
|
|
||||||
|
|
||||||
go 1.14
|
|
5
vendor/github.com/gobuffalo/flect/go.mod
generated
vendored
5
vendor/github.com/gobuffalo/flect/go.mod
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
module github.com/gobuffalo/flect
|
|
||||||
|
|
||||||
go 1.13
|
|
||||||
|
|
||||||
require github.com/stretchr/testify v1.4.0
|
|
11
vendor/github.com/gobuffalo/flect/go.sum
generated
vendored
11
vendor/github.com/gobuffalo/flect/go.sum
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
3
vendor/github.com/google/gofuzz/go.mod
generated
vendored
3
vendor/github.com/google/gofuzz/go.mod
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
module github.com/google/gofuzz
|
|
||||||
|
|
||||||
go 1.12
|
|
1
vendor/github.com/hashicorp/golang-lru/go.mod
generated
vendored
1
vendor/github.com/hashicorp/golang-lru/go.mod
generated
vendored
@@ -1 +0,0 @@
|
|||||||
module github.com/hashicorp/golang-lru
|
|
8
vendor/github.com/josharian/native/doc.go
generated
vendored
Normal file
8
vendor/github.com/josharian/native/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Package native provides easy access to native byte order.
|
||||||
|
//
|
||||||
|
// Usage: use native.Endian where you need the native binary.ByteOrder.
|
||||||
|
//
|
||||||
|
// Please think twice before using this package.
|
||||||
|
// It can break program portability.
|
||||||
|
// Native byte order is usually not the right answer.
|
||||||
|
package native
|
7
vendor/github.com/josharian/native/endian_big.go
generated
vendored
Normal file
7
vendor/github.com/josharian/native/endian_big.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// +build mips mips64 ppc64 s390x
|
||||||
|
|
||||||
|
package native
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
var Endian = binary.BigEndian
|
26
vendor/github.com/josharian/native/endian_generic.go
generated
vendored
Normal file
26
vendor/github.com/josharian/native/endian_generic.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// +build !mips,!mips64,!ppc64,!s390x,!amd64,!386,!arm,!arm64,!mipsle,!mips64le,!ppc64le,!riscv64,!wasm
|
||||||
|
|
||||||
|
// This file is a fallback, so that package native doesn't break
|
||||||
|
// the instant the Go project adds support for a new architecture.
|
||||||
|
//
|
||||||
|
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Endian binary.ByteOrder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
b := uint16(0xff) // one byte
|
||||||
|
if *(*byte)(unsafe.Pointer(&b)) == 0 {
|
||||||
|
Endian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
Endian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
log.Printf("github.com/josharian/native: unrecognized arch %v (%v), please file an issue", runtime.GOARCH, Endian)
|
||||||
|
}
|
7
vendor/github.com/josharian/native/endian_little.go
generated
vendored
Normal file
7
vendor/github.com/josharian/native/endian_little.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// +build amd64 386 arm arm64 mipsle mips64le ppc64le riscv64 wasm
|
||||||
|
|
||||||
|
package native
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
var Endian = binary.LittleEndian
|
7
vendor/github.com/josharian/native/license
generated
vendored
Normal file
7
vendor/github.com/josharian/native/license
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Copyright 2020 Josh Bleecher Snyder
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
10
vendor/github.com/josharian/native/readme.md
generated
vendored
Normal file
10
vendor/github.com/josharian/native/readme.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Package native provides easy access to native byte order.
|
||||||
|
|
||||||
|
`go get github.com/josharian/native`
|
||||||
|
|
||||||
|
Usage: Use `native.Endian` where you need the native binary.ByteOrder.
|
||||||
|
|
||||||
|
Please think twice before using this package.
|
||||||
|
It can break program portability.
|
||||||
|
Native byte order is usually not the right answer.
|
||||||
|
|
11
vendor/github.com/json-iterator/go/go.mod
generated
vendored
11
vendor/github.com/json-iterator/go/go.mod
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
module github.com/json-iterator/go
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/davecgh/go-spew v1.1.1
|
|
||||||
github.com/google/gofuzz v1.0.0
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
|
|
||||||
github.com/stretchr/testify v1.3.0
|
|
||||||
)
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user