diff --git a/build/images/scripts/install_cni b/build/images/scripts/install_cni index 336e53cb031..a464662efdc 100755 --- a/build/images/scripts/install_cni +++ b/build/images/scripts/install_cni @@ -33,5 +33,9 @@ install -m 755 /opt/cni/bin/whereabouts /host/opt/cni/bin/whereabouts # Load the OVS kernel module modprobe openvswitch || (echo "Failed to load the OVS kernel module from the container, try running 'modprobe openvswitch' on your Nodes"; exit 1) +# Load the WireGuard kernel module. This is only required when WireGuard encryption is enabled. +# We could parse the antrea config file in the init-container to dynamically load this kernel module in the future. +modprobe wireguard || (echo "Failed to load the WireGuard kernel module, WireGuard encryption will not be available") + # Change the default permissions of the run directory. chmod 0750 /var/run/antrea diff --git a/build/images/wireguard-go/Dockerfile b/build/images/wireguard-go/Dockerfile new file mode 100644 index 00000000000..6d086f877c6 --- /dev/null +++ b/build/images/wireguard-go/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.16 as builder + +RUN git clone https://git.zx2c4.com/wireguard-go && \ + cd wireguard-go && \ + make && \ + make install + +RUN git clone https://git.zx2c4.com/wireguard-tools && \ + cd wireguard-tools && \ + cd src && \ + make && \ + make install + +FROM ubuntu:20.04 +COPY --from=builder /usr/bin/wireguard-go /usr/bin/wg /usr/bin/ diff --git a/build/images/wireguard-go/README.md b/build/images/wireguard-go/README.md new file mode 100644 index 00000000000..8548042be9f --- /dev/null +++ b/build/images/wireguard-go/README.md @@ -0,0 +1,17 @@ +# images/wireguard-go + +This Docker image is a very lightweight image based on Ubuntu 20.04 which +includes WireGuard golang implementation and wireguard-tools. It can be used +for Kind clusters for tests when injected as a sidecar to antrea-agent. + +If you need to build a new version of the image and push it to Dockerhub, you +can run the following: + +```bash +cd build/images/wireguard-go +docker build -t antrea/wireguard-go:latest . +docker push antrea/wireguard-go:latest +``` + +The `docker push` command will fail if you do not have permission to push to the +`antrea` Dockerhub repository. diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml index 0a6f01ae2b4..fd425b2f754 100644 --- a/build/yamls/antrea-aks.yml +++ b/build/yamls/antrea-aks.yml @@ -3801,6 +3801,9 @@ data: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 + # Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + #wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -3832,8 +3835,18 @@ data: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 - # Whether or not to enable IPsec encryption of tunnel traffic. - #enableIPSecTunnel: false + # Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. + # It has the following options: + # - none (default): Inter-node Pod traffic will not be encrypted. + # - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + # Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + # the PSK value must be passed to Antrea Agent through an environment + # variable: ANTREA_IPSEC_PSK. + # - wireguard: Enable WireGuard for tunnel traffic encryption. + #trafficEncryptionMode: none + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by @@ -3983,7 +3996,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 namespace: kube-system --- apiVersion: v1 @@ -4054,7 +4067,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-66dt98cgtb + value: antrea-config-b8dc5gg2d2 image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -4105,7 +4118,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 name: antrea-config - name: antrea-controller-tls secret: @@ -4386,7 +4399,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml index 518f90f45d7..581d543f65c 100644 --- a/build/yamls/antrea-eks.yml +++ b/build/yamls/antrea-eks.yml @@ -3801,6 +3801,9 @@ data: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 + # Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + #wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -3832,8 +3835,18 @@ data: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 - # Whether or not to enable IPsec encryption of tunnel traffic. - #enableIPSecTunnel: false + # Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. + # It has the following options: + # - none (default): Inter-node Pod traffic will not be encrypted. + # - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + # Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + # the PSK value must be passed to Antrea Agent through an environment + # variable: ANTREA_IPSEC_PSK. + # - wireguard: Enable WireGuard for tunnel traffic encryption. + #trafficEncryptionMode: none + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by @@ -3983,7 +3996,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 namespace: kube-system --- apiVersion: v1 @@ -4054,7 +4067,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-66dt98cgtb + value: antrea-config-b8dc5gg2d2 image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -4105,7 +4118,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 name: antrea-config - name: antrea-controller-tls secret: @@ -4388,7 +4401,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-66dt98cgtb + name: antrea-config-b8dc5gg2d2 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml index 0150f013609..e32f0453bce 100644 --- a/build/yamls/antrea-gke.yml +++ b/build/yamls/antrea-gke.yml @@ -3801,6 +3801,9 @@ data: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 + # Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + #wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -3832,8 +3835,18 @@ data: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 - # Whether or not to enable IPsec encryption of tunnel traffic. - #enableIPSecTunnel: false + # Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. + # It has the following options: + # - none (default): Inter-node Pod traffic will not be encrypted. + # - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + # Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + # the PSK value must be passed to Antrea Agent through an environment + # variable: ANTREA_IPSEC_PSK. + # - wireguard: Enable WireGuard for tunnel traffic encryption. + #trafficEncryptionMode: none + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 # ClusterIP CIDR range for IPv6 Services. It's required when using kube-proxy to provide IPv6 Service in a Dual-Stack # cluster or an IPv6 only cluster. The value should be the same as the configuration for kube-apiserver specified by @@ -3983,7 +3996,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-d2f597tg62 + name: antrea-config-242t7c74gb namespace: kube-system --- apiVersion: v1 @@ -4054,7 +4067,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-d2f597tg62 + value: antrea-config-242t7c74gb image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -4105,7 +4118,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-d2f597tg62 + name: antrea-config-242t7c74gb name: antrea-config - name: antrea-controller-tls secret: @@ -4389,7 +4402,7 @@ spec: path: /home/kubernetes/bin name: host-cni-bin - configMap: - name: antrea-config-d2f597tg62 + name: antrea-config-242t7c74gb name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml index 9b71c9a0c02..cdf646f4f0e 100644 --- a/build/yamls/antrea-ipsec.yml +++ b/build/yamls/antrea-ipsec.yml @@ -3801,6 +3801,9 @@ data: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 + # Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + #wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -3832,8 +3835,18 @@ data: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 - # Whether or not to enable IPsec encryption of tunnel traffic. - enableIPSecTunnel: true + # Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. + # It has the following options: + # - none (default): Inter-node Pod traffic will not be encrypted. + # - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + # Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + # the PSK value must be passed to Antrea Agent through an environment + # variable: ANTREA_IPSEC_PSK. + # - wireguard: Enable WireGuard for tunnel traffic encryption. + trafficEncryptionMode: ipsec + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When @@ -3988,7 +4001,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-bgd79km9c8 + name: antrea-config-fk2557bh4f namespace: kube-system --- apiVersion: v1 @@ -4068,7 +4081,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-bgd79km9c8 + value: antrea-config-fk2557bh4f image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -4119,7 +4132,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-bgd79km9c8 + name: antrea-config-fk2557bh4f name: antrea-config - name: antrea-controller-tls secret: @@ -4435,7 +4448,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-bgd79km9c8 + name: antrea-config-fk2557bh4f name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml index 642c5f2b5e2..85e4559e4e4 100644 --- a/build/yamls/antrea.yml +++ b/build/yamls/antrea.yml @@ -3801,6 +3801,9 @@ data: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 + # Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + #wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -3832,8 +3835,18 @@ data: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 - # Whether or not to enable IPsec encryption of tunnel traffic. - #enableIPSecTunnel: false + # Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. + # It has the following options: + # - none (default): Inter-node Pod traffic will not be encrypted. + # - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + # Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + # the PSK value must be passed to Antrea Agent through an environment + # variable: ANTREA_IPSEC_PSK. + # - wireguard: Enable WireGuard for tunnel traffic encryption. + #trafficEncryptionMode: none + + # The port for WireGuard to receive traffic. + #wireGuardPort: 51850 # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When @@ -3988,7 +4001,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-dd8ffc8tk9 + name: antrea-config-f2868hh5ch namespace: kube-system --- apiVersion: v1 @@ -4059,7 +4072,7 @@ spec: fieldRef: fieldPath: spec.serviceAccountName - name: ANTREA_CONFIG_MAP_NAME - value: antrea-config-dd8ffc8tk9 + value: antrea-config-f2868hh5ch image: projects.registry.vmware.com/antrea/antrea-ubuntu:latest imagePullPolicy: IfNotPresent livenessProbe: @@ -4110,7 +4123,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-dd8ffc8tk9 + name: antrea-config-f2868hh5ch name: antrea-config - name: antrea-controller-tls secret: @@ -4391,7 +4404,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-dd8ffc8tk9 + name: antrea-config-f2868hh5ch name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/base/conf/antrea-agent.conf b/build/yamls/base/conf/antrea-agent.conf index 36193814a3c..b3e2edfb173 100644 --- a/build/yamls/base/conf/antrea-agent.conf +++ b/build/yamls/base/conf/antrea-agent.conf @@ -46,6 +46,9 @@ featureGates: # Make sure it doesn't conflict with your existing interfaces. #hostGateway: antrea-gw0 +# Name of the interface antrea-agent will create and use for WireGuard traffic encryption. +#wireGuardInterfaceName: antrea-wg0 + # Determines how traffic is encapsulated. It has the following options: # encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network # traffic is SNAT'd. @@ -77,8 +80,18 @@ featureGates: # also adjust MTU to accommodate for tunnel encapsulation overhead (if applicable). #defaultMTU: 0 -# Whether or not to enable IPsec encryption of tunnel traffic. -#enableIPSecTunnel: false +# Determines how tunnel traffic is encrypted. Currently encryption only works with antrea encap mode. +# It has the following options: +# - none (default): Inter-node Pod traffic will not be encrypted. +# - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses +# Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, +# the PSK value must be passed to Antrea Agent through an environment +# variable: ANTREA_IPSEC_PSK. +# - wireguard: Enable WireGuard for tunnel traffic encryption. +#trafficEncryptionMode: none + +# The port for WireGuard to receive traffic. +#wireGuardPort: 51850 # ClusterIP CIDR range for Services. It's required when AntreaProxy is not enabled, and should be # set to the same value as the one specified by --service-cluster-ip-range for kube-apiserver. When diff --git a/build/yamls/patches/kind/wireguardGo.yml b/build/yamls/patches/kind/wireguardGo.yml new file mode 100644 index 00000000000..d839162fca2 --- /dev/null +++ b/build/yamls/patches/kind/wireguardGo.yml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: antrea-agent +spec: + template: + spec: + containers: + - name: wireguard + command: + - wireguard-go + args: + - -f + - antrea-wg0 + image: xliuxu/wireguard-go:latest + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: 100m + securityContext: + capabilities: + add: + - NET_ADMIN + volumeMounts: + - name: host-var-run-wireguard + mountPath: /var/run/wireguard + - name: dev-tun + mountPath: /dev/net/tun + - name: antrea-agent + volumeMounts: + - name: host-var-run-wireguard + mountPath: /var/run/wireguard + volumes: + - name: host-var-run-wireguard + hostPath: + path: /var/run/wireguard + type: DirectoryOrCreate + - name: dev-tun + hostPath: + path: /dev/net/tun + type: CharDevice diff --git a/ci/kind/test-e2e-kind.sh b/ci/kind/test-e2e-kind.sh index 0e8e9a429b9..f0e88bd08b8 100755 --- a/ci/kind/test-e2e-kind.sh +++ b/ci/kind/test-e2e-kind.sh @@ -89,7 +89,7 @@ case $key in --coverage) coverage=true shift - ;; + ;; -h|--help) print_usage exit 0 @@ -112,7 +112,12 @@ if ! $np; then manifest_args="$manifest_args --no-np" fi -COMMON_IMAGES_LIST=("gcr.io/kubernetes-e2e-test-images/agnhost:2.8" "projects.registry.vmware.com/library/busybox" "projects.registry.vmware.com/antrea/nginx" "projects.registry.vmware.com/antrea/perftool" "projects.registry.vmware.com/antrea/ipfix-collector:v0.5.4") +COMMON_IMAGES_LIST=("gcr.io/kubernetes-e2e-test-images/agnhost:2.8" \ + "projects.registry.vmware.com/library/busybox" \ + "projects.registry.vmware.com/antrea/nginx" \ + "projects.registry.vmware.com/antrea/perftool" \ + "projects.registry.vmware.com/antrea/ipfix-collector:v0.5.4" \ + "xliuxu/wireguard-go") for image in "${COMMON_IMAGES_LIST[@]}"; do for i in `seq 3`; do docker pull $image && break @@ -147,11 +152,14 @@ function run_test { if $coverage; then $YML_CMD --kind --encap-mode $current_mode $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea-coverage.yml + $YML_CMD --kind --encap-mode $current_mode --wireguard-go $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea-wireguard-go-coverage.yml $FLOWAGGREGATOR_YML_CMD --coverage | docker exec -i kind-control-plane dd of=/root/flow-aggregator-coverage.yml else $YML_CMD --kind --encap-mode $current_mode $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea.yml + $YML_CMD --kind --encap-mode $current_mode --wireguard-go $manifest_args | docker exec -i kind-control-plane dd of=/root/antrea-wireguard-go.yml $FLOWAGGREGATOR_YML_CMD | docker exec -i kind-control-plane dd of=/root/flow-aggregator.yml fi + sleep 1 if $coverage; then diff --git a/cmd/antrea-agent/agent.go b/cmd/antrea-agent/agent.go index 8d72bca6da6..af3b86a7f7d 100644 --- a/cmd/antrea-agent/agent.go +++ b/cmd/antrea-agent/agent.go @@ -114,11 +114,21 @@ func run(o *Options) error { } _, encapMode := config.GetTrafficEncapModeFromStr(o.config.TrafficEncapMode) + _, encryptionMode := config.GetTrafficEncryptionModeFromStr(o.config.TrafficEncryptionMode) + if o.config.EnableIPSecTunnel { + klog.Warning("enableIPSecTunnel is deprecated. Use trafficEncryptionMode instead.") + encryptionMode = config.TrafficEncryptionModeIPSec + } networkConfig := &config.NetworkConfig{ - TunnelType: ovsconfig.TunnelType(o.config.TunnelType), - TrafficEncapMode: encapMode, - EnableIPSecTunnel: o.config.EnableIPSecTunnel, - TransportIface: o.config.TransportInterface, + TunnelType: ovsconfig.TunnelType(o.config.TunnelType), + TrafficEncapMode: encapMode, + TrafficEncryptionMode: encryptionMode, + TransportIface: o.config.TransportInterface, + } + + wireguardConfig := &config.WireGuardConfig{ + Name: o.config.WireGuardInterfaceName, + WireGuardPort: o.config.WireGuardPort, } routeClient, err := route.NewClient(serviceCIDRNet, networkConfig, o.config.NoSNAT) @@ -149,6 +159,7 @@ func run(o *Options) error { serviceCIDRNet, serviceCIDRNetv6, networkConfig, + wireguardConfig, networkReadyCh, stopCh, features.DefaultFeatureGate.Enabled(features.AntreaProxy)) @@ -166,7 +177,8 @@ func run(o *Options) error { routeClient, ifaceStore, networkConfig, - nodeConfig) + nodeConfig, + agentInitializer.GetWireGuardClient()) var proxier proxy.Proxier if features.DefaultFeatureGate.Enabled(features.AntreaProxy) { diff --git a/cmd/antrea-agent/config.go b/cmd/antrea-agent/config.go index d3c3a0c2582..62ff5bd843e 100644 --- a/cmd/antrea-agent/config.go +++ b/cmd/antrea-agent/config.go @@ -48,6 +48,9 @@ type AgentConfig struct { // Make sure it doesn't conflict with your existing interfaces. // Defaults to antrea-gw0. HostGateway string `yaml:"hostGateway,omitempty"` + // Name of the interface antrea-agent will create and use for WireGuard traffic encryption. + // Defaults to antrea-wg0. + WireGuardInterfaceName string `yaml:"wireGuardInterfaceName,omitempty"` // Determines how traffic is encapsulated. It has the following options: // encap(default): Inter-node Pod traffic is always encapsulated and Pod to external network // traffic is SNAT'd. @@ -89,11 +92,20 @@ type AgentConfig struct { // --service-cluster-ip-range. When AntreaProxy is enabled, this parameter is not needed. // No default value for this field. ServiceCIDRv6 string `yaml:"serviceCIDRv6,omitempty"` - // Whether or not to enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses Preshared Key (PSK) for IKE - // authentication. When IPSec tunnel is enabled, the PSK value must be passed to Antrea Agent - // through an environment variable: ANTREA_IPSEC_PSK. - // Defaults to false. + // Deprecated. Use TrafficEncryptionMode instead. EnableIPSecTunnel bool `yaml:"enableIPSecTunnel,omitempty"` + // Determines how tunnel traffic is encrypted. + // It has the following options: + // - none (default): Inter-node Pod traffic will not be encrypted. + // - ipsec: Enable IPSec (ESP) encryption for Pod traffic across Nodes. Antrea uses + // Preshared Key (PSK) for IKE authentication. When IPSec tunnel is enabled, + // the PSK value must be passed to Antrea Agent through an environment + // variable: ANTREA_IPSEC_PSK. + // - wireguard: Enable WireGuard for tunnel traffic encryption. + TrafficEncryptionMode string `yaml:"trafficEncryptionMode,omitempty"` + // WireGuardPort is the port for the WireGuard to receive traffic. + // Defaults to 51850. + WireGuardPort int `yaml:"wireGuardPort,omitempty"` // APIPort is the port for the antrea-agent APIServer to serve on. // Defaults to 10350. APIPort int `yaml:"apiPort,omitempty"` diff --git a/cmd/antrea-agent/options.go b/cmd/antrea-agent/options.go index b6a28e02a36..38325201c76 100644 --- a/cmd/antrea-agent/options.go +++ b/cmd/antrea-agent/options.go @@ -35,6 +35,7 @@ import ( const ( defaultOVSBridge = "br-int" defaultHostGateway = "antrea-gw0" + defaultWireGuardInterfaceName = "antrea-wg0" defaultHostProcPathPrefix = "/host" defaultServiceCIDR = "10.96.0.0/12" defaultTunnelType = ovsconfig.GeneveTunnel @@ -116,6 +117,10 @@ func (o *Options) validate(args []string) error { o.config.TunnelType != ovsconfig.GRETunnel && o.config.TunnelType != ovsconfig.STTTunnel { return fmt.Errorf("tunnel type %s is invalid", o.config.TunnelType) } + ok, encryptionMode := config.GetTrafficEncryptionModeFromStr(o.config.TrafficEncryptionMode) + if !ok { + return fmt.Errorf("TrafficEncryptionMode %s is unknown", o.config.TrafficEncryptionMode) + } if o.config.OVSDatapathType != string(ovsconfig.OVSDatapathSystem) && o.config.OVSDatapathType != string(ovsconfig.OVSDatapathNetdev) { return fmt.Errorf("OVS datapath type %s is not supported", o.config.OVSDatapathType) } @@ -134,8 +139,8 @@ func (o *Options) validate(args []string) error { if !features.DefaultFeatureGate.Enabled(features.AntreaProxy) { return fmt.Errorf("TrafficEncapMode %s requires AntreaProxy to be enabled", o.config.TrafficEncapMode) } - if o.config.EnableIPSecTunnel { - return fmt.Errorf("IPsec tunnel may only be enabled in %s mode", config.TrafficEncapModeEncap) + if encryptionMode == config.TrafficEncryptionModeIPSec { + return fmt.Errorf("TrafficEncryptionMode %s may only be enabled in %s mode", encryptionMode, config.TrafficEncapModeEncap) } } if o.config.NoSNAT && !(encapMode == config.TrafficEncapModeNoEncap || encapMode == config.TrafficEncapModeNetworkPolicyOnly) { @@ -177,9 +182,15 @@ func (o *Options) setDefaults() { if o.config.HostGateway == "" { o.config.HostGateway = defaultHostGateway } + if o.config.WireGuardInterfaceName == "" { + o.config.WireGuardInterfaceName = defaultWireGuardInterfaceName + } if o.config.TrafficEncapMode == "" { o.config.TrafficEncapMode = config.TrafficEncapModeEncap.String() } + if o.config.TrafficEncryptionMode == "" { + o.config.TrafficEncryptionMode = config.TrafficEncryptionModeNone.String() + } if o.config.TunnelType == "" { o.config.TunnelType = defaultTunnelType } @@ -192,10 +203,12 @@ func (o *Options) setDefaults() { if o.config.APIPort == 0 { o.config.APIPort = apis.AntreaAgentAPIPort } - if o.config.ClusterMembershipPort == 0 { o.config.ClusterMembershipPort = apis.AntreaAgentClusterMembershipPort } + if o.config.WireGuardPort == 0 { + o.config.WireGuardPort = apis.WireGuardListenPort + } if features.DefaultFeatureGate.Enabled(features.FlowExporter) { if o.config.FlowCollectorAddr == "" { diff --git a/cmd/antrea-agent/options_windows.go b/cmd/antrea-agent/options_windows.go index 1e72a509201..2bcefbf7d67 100644 --- a/cmd/antrea-agent/options_windows.go +++ b/cmd/antrea-agent/options_windows.go @@ -48,10 +48,10 @@ func (o *Options) checkUnsupportedFeatures() error { if o.config.TunnelType == ovsconfig.GRETunnel { unsupported = append(unsupported, "TunnelType: "+o.config.TunnelType) } - if o.config.EnableIPSecTunnel { - unsupported = append(unsupported, "IPsecTunnel") + _, encryptionMode := config.GetTrafficEncryptionModeFromStr(o.config.TrafficEncryptionMode) + if encryptionMode != config.TrafficEncryptionModeNone { + unsupported = append(unsupported, "TrafficEncryptionMode: "+encryptionMode.String()) } - if unsupported != nil { return fmt.Errorf("unsupported features on Windows: {%s}", strings.Join(unsupported, ", ")) } diff --git a/cmd/antrea-agent/options_windows_test.go b/cmd/antrea-agent/options_windows_test.go index f27632b281b..efab0ca9fa0 100644 --- a/cmd/antrea-agent/options_windows_test.go +++ b/cmd/antrea-agent/options_windows_test.go @@ -65,8 +65,13 @@ func TestCheckUnsupportedFeatures(t *testing.T) { false, }, { - "IPsec tunnel", - AgentConfig{EnableIPSecTunnel: true}, + "IPsec encryption", + AgentConfig{TrafficEncryptionMode: config.TrafficEncryptionModeIPSec.String()}, + false, + }, + { + "WireGuard encryption", + AgentConfig{TrafficEncryptionMode: config.TrafficEncryptionModeWireGuard.String()}, false, }, { diff --git a/go.mod b/go.mod index b09b6cd8ec8..216d7ab7c81 100644 --- a/go.mod +++ b/go.mod @@ -42,15 +42,16 @@ require ( github.com/streamrail/concurrent-map v0.0.0-20160823150647-8bf1e9bacbf6 // indirect github.com/stretchr/testify v1.7.0 github.com/ti-mo/conntrack v0.3.0 - github.com/vishvananda/netlink v1.1.0 + github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf github.com/vmware/go-ipfix v0.5.7 - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 + golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 golang.org/x/mod v0.4.2 - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 + golang.org/x/net v0.0.0-20210504132125-bbd867fde50d golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210510120138-977fb7262007 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 google.golang.org/grpc v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 9a5b09a32d0..12cac362bd9 100644 --- a/go.sum +++ b/go.sum @@ -302,6 +302,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.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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -378,9 +379,16 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6t github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= 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/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw= 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 h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do= +github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= 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.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= @@ -436,14 +444,26 @@ github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vq 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/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 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg= 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 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0= +github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -608,11 +628,13 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf h1:JMdq3oWN6LQKfRpwVfjwuaZcPN4vGdVBzvOU2mzUXd8= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/go-ipfix v0.5.7 h1:twv8mrXwlXhlWdixogGmyKwLia/G7hsZM46xd8aO+rc= github.com/vmware/go-ipfix v0.5.7/go.mod h1:yzbG1rv+yJ8GeMrRm+MDhOV3akygNZUHLhC1pDoD2AY= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -661,8 +683,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-20200820211705-5c72a883971a/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 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -733,11 +756,17 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/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-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-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +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-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -793,18 +822,30 @@ golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/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-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-20201201145000-ef89a241ccb3/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-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-20210309040221-94ec62e08169/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-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -816,8 +857,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -874,6 +916,10 @@ 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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b h1:XDLXhn7ryprJVo+Lpkiib6CIuXE2031GDwtfEm7vLjI= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 h1:LpEwXnbN4q2EIPkqbG9KHBUrducJYDOOdL+eMcJAlFo= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/hack/generate-manifest.sh b/hack/generate-manifest.sh index 1ecdf687406..0eee33cc7c7 100755 --- a/hack/generate-manifest.sh +++ b/hack/generate-manifest.sh @@ -44,6 +44,7 @@ Generate a YAML manifest for Antrea using Kustomize and print it to stdout. --custom-adm-controller Generates a manifest with custom Antrea admission controller to validate/mutate resources. --hw-offload Generates a manifest with hw-offload enabled in the antrea-ovs container. --sriov Generates a manifest which enables use of Kubelet API for SR-IOV device info. + --wireguard-go Generate a manifest with WireGuard (golang implementation) encryption enabled --help, -h Print this message and exit In 'release' mode, environment variables IMG_NAME and IMG_TAG must be set. @@ -84,6 +85,7 @@ SIMULATOR=false CUSTOM_ADM_CONTROLLER=false HW_OFFLOAD=false SRIOV=false +WIREGUARDGO=false while [[ $# -gt 0 ]] do @@ -170,6 +172,10 @@ case $key in --sriov) SRIOV=true shift + ;; + --wireguard-go) + WIREGUARDGO=true + shift ;; -h|--help) print_usage @@ -260,11 +266,15 @@ if $KIND; then fi if $IPSEC; then - sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*enableIPSecTunnel[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/enableIPSecTunnel: true/" antrea-agent.conf + sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*trafficEncryptionMode[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/trafficEncryptionMode: ipsec/" antrea-agent.conf # change the tunnel type to GRE which works better with IPSec encryption than other types. sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*tunnelType[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/tunnelType: gre/" antrea-agent.conf fi +if $WIREGUARDGO; then + sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*trafficEncryptionMode[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/trafficEncryptionMode: wireguard/" antrea-agent.conf +fi + if $ALLFEATURES; then sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*AntreaPolicy[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/ AntreaPolicy: true/" antrea-agent.conf sed -i.bak -E "s/^[[:space:]]*#[[:space:]]*FlowExporter[[:space:]]*:[[:space:]]*[a-z]+[[:space:]]*$/ FlowExporter: true/" antrea-agent.conf @@ -410,6 +420,11 @@ if $KIND; then # change initContainer script and remove SYS_MODULE capability $KUSTOMIZE edit add patch --path installCni.yml + # inject the wireguard-go container to run WireGuard in userspace + if $WIREGUARDGO; then + $KUSTOMIZE edit add patch --path wireguardGo.yml + fi + if $ON_DELETE; then $KUSTOMIZE edit add patch --path onDeleteUpdateStrategy.yml fi diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 273fecb4082..16ad626e33a 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -41,6 +41,7 @@ import ( "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" "antrea.io/antrea/pkg/agent/util" + "antrea.io/antrea/pkg/agent/wireguard" "antrea.io/antrea/pkg/features" "antrea.io/antrea/pkg/ovs/ovsconfig" "antrea.io/antrea/pkg/ovs/ovsctl" @@ -71,6 +72,7 @@ type Initializer struct { ovsBridgeClient ovsconfig.OVSBridgeClient ofClient openflow.Client routeClient route.Interface + wireGuardClient wireguard.Interface ifaceStore interfacestore.InterfaceStore ovsBridge string hostGateway string // name of gateway port on the OVS bridge @@ -79,6 +81,7 @@ type Initializer struct { serviceCIDRv6 *net.IPNet // K8s Service ClusterIP CIDR in IPv6 networkConfig *config.NetworkConfig nodeConfig *config.NodeConfig + wireGuardConfig *config.WireGuardConfig enableProxy bool // networkReadyCh should be closed once the Node's network is ready. // The CNI server will wait for it before handling any CNI Add requests. @@ -98,6 +101,7 @@ func NewInitializer( serviceCIDR *net.IPNet, serviceCIDRv6 *net.IPNet, networkConfig *config.NetworkConfig, + wireGuardConfig *config.WireGuardConfig, networkReadyCh chan<- struct{}, stopCh <-chan struct{}, enableProxy bool) *Initializer { @@ -113,6 +117,7 @@ func NewInitializer( serviceCIDR: serviceCIDR, serviceCIDRv6: serviceCIDRv6, networkConfig: networkConfig, + wireGuardConfig: wireGuardConfig, networkReadyCh: networkReadyCh, stopCh: stopCh, enableProxy: enableProxy, @@ -124,6 +129,11 @@ func (i *Initializer) GetNodeConfig() *config.NodeConfig { return i.nodeConfig } +// GetNodeConfig returns the NodeConfig. +func (i *Initializer) GetWireGuardClient() wireguard.Interface { + return i.wireGuardClient +} + // setupOVSBridge sets up the OVS bridge and create host gateway interface and tunnel port func (i *Initializer) setupOVSBridge() error { if err := i.ovsBridgeClient.Create(); err != nil { @@ -258,8 +268,15 @@ func (i *Initializer) Initialize() error { return err } - if err := i.initializeIPSec(); err != nil { - return err + switch i.networkConfig.TrafficEncryptionMode { + case config.TrafficEncryptionModeIPSec: + if err := i.initializeIPSec(); err != nil { + return err + } + case config.TrafficEncryptionModeWireGuard: + if err := i.initializeWireGuard(); err != nil { + return err + } } if err := i.prepareHostNetwork(); err != nil { @@ -336,7 +353,7 @@ func (i *Initializer) initOpenFlowPipeline() error { roundInfo := getRoundInfo(i.ovsBridgeClient) // Set up all basic flows. - ofConnCh, err := i.ofClient.Initialize(roundInfo, i.nodeConfig, i.networkConfig.TrafficEncapMode) + ofConnCh, err := i.ofClient.Initialize(roundInfo, i.nodeConfig, i.networkConfig) if err != nil { klog.Errorf("Failed to initialize openflow client: %v", err) return err @@ -719,12 +736,15 @@ func (i *Initializer) initNodeLocalConfig() error { } i.nodeConfig = &config.NodeConfig{ - Name: nodeName, - OVSBridge: i.ovsBridge, - DefaultTunName: defaultTunInterfaceName, - NodeIPAddr: nodeIPAddr, - NodeTransportIPAddr: transportIPAddr, - UplinkNetConfig: new(config.AdapterNetConfig)} + Name: nodeName, + OVSBridge: i.ovsBridge, + DefaultTunName: defaultTunInterfaceName, + NodeIPAddr: nodeIPAddr, + NodeTransportIPAddr: transportIPAddr, + UplinkNetConfig: new(config.AdapterNetConfig), + NodeLocalInterfaceMTU: localIntf.MTU, + WireGuardConfig: i.wireGuardConfig, + } mtu, err := i.getNodeMTU(localIntf) if err != nil { @@ -784,10 +804,6 @@ func (i *Initializer) initNodeLocalConfig() error { // initializeIPSec checks if preconditions are met for using IPsec and reads the IPsec PSK value. func (i *Initializer) initializeIPSec() error { - if !i.networkConfig.EnableIPSecTunnel { - return nil - } - // At the time the agent is initialized and this code is executed, the // OVS daemons are already running given that we have successfully // connected to OVSDB. Given that the start_ovs script deletes existing @@ -817,6 +833,18 @@ func (i *Initializer) initializeIPSec() error { return nil } +// initializeWireguard checks if preconditions are met for using WireGuard and initializes WireGuard client or cleans up. +func (i *Initializer) initializeWireGuard() error { + i.wireGuardConfig.MTU = i.nodeConfig.NodeLocalInterfaceMTU - config.WireGuardOverhead + wgClient, err := wireguard.New(i.client, i.nodeConfig, i.wireGuardConfig) + if err != nil { + return err + } + + i.wireGuardClient = wgClient + return i.wireGuardClient.Init() +} + // readIPSecPSK reads the IPsec PSK value from environment variable ANTREA_IPSEC_PSK func (i *Initializer) readIPSecPSK() error { i.networkConfig.IPSecPSK = os.Getenv(ipsecPSKEnvKey) @@ -897,11 +925,12 @@ func (i *Initializer) getNodeMTU(localIntf *net.Interface) (int, error) { } else if i.networkConfig.TunnelType == ovsconfig.GRETunnel { mtu -= config.GREOverhead } + if i.nodeConfig.NodeIPAddr.IP.To4() == nil { mtu -= config.IPv6ExtraOverhead } } - if i.networkConfig.EnableIPSecTunnel { + if i.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeIPSec { mtu -= config.IPSecESPOverhead } return mtu, nil diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index b5741a52aca..b30f353256d 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -174,84 +174,93 @@ func TestInitNodeLocalConfig(t *testing.T) { ipNet: transportIPNet, } tests := []struct { - name string - trafficEncapMode config.TrafficEncapModeType - transportInterface *testTransInterface - tunnelType ovsconfig.TunnelType - mtu int - expectedMTU int - expectedNodeAnnotation map[string]string + name string + trafficEncapMode config.TrafficEncapModeType + transportInterface *testTransInterface + tunnelType ovsconfig.TunnelType + mtu int + expectedMTU int + expectedNodeLocalIfaceMTU int + expectedNodeAnnotation map[string]string }{ { - name: "noencap mode", - trafficEncapMode: config.TrafficEncapModeNoEncap, - mtu: 0, - expectedMTU: 1500, - expectedNodeAnnotation: map[string]string{types.NodeMACAddressAnnotationKey: macAddr.String()}, + name: "noencap mode", + trafficEncapMode: config.TrafficEncapModeNoEncap, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1500, + expectedNodeAnnotation: map[string]string{types.NodeMACAddressAnnotationKey: macAddr.String()}, }, { - name: "hybrid mode", - trafficEncapMode: config.TrafficEncapModeHybrid, - mtu: 0, - expectedMTU: 1500, - expectedNodeAnnotation: map[string]string{types.NodeMACAddressAnnotationKey: macAddr.String()}, + name: "hybrid mode", + trafficEncapMode: config.TrafficEncapModeHybrid, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1500, + expectedNodeAnnotation: map[string]string{types.NodeMACAddressAnnotationKey: macAddr.String()}, }, { - name: "encap mode, geneve tunnel", - trafficEncapMode: config.TrafficEncapModeEncap, - tunnelType: ovsconfig.GeneveTunnel, - mtu: 0, - expectedMTU: 1450, - expectedNodeAnnotation: nil, + name: "encap mode, geneve tunnel", + trafficEncapMode: config.TrafficEncapModeEncap, + tunnelType: ovsconfig.GeneveTunnel, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1450, + expectedNodeAnnotation: nil, }, { - name: "encap mode, mtu specified", - trafficEncapMode: config.TrafficEncapModeEncap, - tunnelType: ovsconfig.GeneveTunnel, - mtu: 1400, - expectedMTU: 1400, - expectedNodeAnnotation: nil, + name: "encap mode, mtu specified", + trafficEncapMode: config.TrafficEncapModeEncap, + tunnelType: ovsconfig.GeneveTunnel, + mtu: 1400, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1400, + expectedNodeAnnotation: nil, }, { - name: "noencap mode with transportInterface", - trafficEncapMode: config.TrafficEncapModeNoEncap, - transportInterface: testTransportIface, - mtu: 0, - expectedMTU: 1500, + name: "noencap mode with transportInterface", + trafficEncapMode: config.TrafficEncapModeNoEncap, + transportInterface: testTransportIface, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1500, expectedNodeAnnotation: map[string]string{ types.NodeMACAddressAnnotationKey: transportIfaceMAC.String(), types.NodeTransportAddressAnnotationKey: transportIP.String(), }, }, { - name: "hybrid mode with transportInterface", - trafficEncapMode: config.TrafficEncapModeHybrid, - transportInterface: testTransportIface, - mtu: 0, - expectedMTU: 1500, + name: "hybrid mode with transportInterface", + trafficEncapMode: config.TrafficEncapModeHybrid, + transportInterface: testTransportIface, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1500, expectedNodeAnnotation: map[string]string{ types.NodeMACAddressAnnotationKey: transportIfaceMAC.String(), types.NodeTransportAddressAnnotationKey: transportIP.String(), }, }, { - name: "encap mode with transportInterface, geneve tunnel", - trafficEncapMode: config.TrafficEncapModeEncap, - transportInterface: testTransportIface, - tunnelType: ovsconfig.GeneveTunnel, - mtu: 0, - expectedMTU: 1450, + name: "encap mode with transportInterface, geneve tunnel", + trafficEncapMode: config.TrafficEncapModeEncap, + transportInterface: testTransportIface, + tunnelType: ovsconfig.GeneveTunnel, + mtu: 0, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1450, expectedNodeAnnotation: map[string]string{ types.NodeTransportAddressAnnotationKey: transportIP.String(), }, }, { - name: "encap mode with transportInterface, mtu specified", - trafficEncapMode: config.TrafficEncapModeEncap, - transportInterface: testTransportIface, - tunnelType: ovsconfig.GeneveTunnel, - mtu: 1400, - expectedMTU: 1400, + name: "encap mode with transportInterface, mtu specified", + trafficEncapMode: config.TrafficEncapModeEncap, + transportInterface: testTransportIface, + tunnelType: ovsconfig.GeneveTunnel, + mtu: 1400, + expectedNodeLocalIfaceMTU: 1500, + expectedMTU: 1400, expectedNodeAnnotation: map[string]string{ types.NodeTransportAddressAnnotationKey: transportIP.String(), }, @@ -278,14 +287,15 @@ func TestInitNodeLocalConfig(t *testing.T) { client := fake.NewSimpleClientset(node) ifaceStore := interfacestore.NewInterfaceStore() expectedNodeConfig := config.NodeConfig{ - Name: nodeName, - OVSBridge: ovsBridge, - DefaultTunName: defaultTunInterfaceName, - PodIPv4CIDR: podCIDR, - NodeIPAddr: nodeIPNet, - NodeTransportIPAddr: nodeIPNet, - NodeMTU: tt.expectedMTU, - UplinkNetConfig: new(config.AdapterNetConfig), + Name: nodeName, + OVSBridge: ovsBridge, + DefaultTunName: defaultTunInterfaceName, + PodIPv4CIDR: podCIDR, + NodeIPAddr: nodeIPNet, + NodeTransportIPAddr: nodeIPNet, + NodeLocalInterfaceMTU: tt.expectedNodeLocalIfaceMTU, + NodeMTU: tt.expectedMTU, + UplinkNetConfig: new(config.AdapterNetConfig), } initializer := &Initializer{ diff --git a/pkg/agent/config/node_config.go b/pkg/agent/config/node_config.go index 750b4ba2ee3..5acaf2058b7 100644 --- a/pkg/agent/config/node_config.go +++ b/pkg/agent/config/node_config.go @@ -34,9 +34,10 @@ const ( ) const ( - VXLANOverhead = 50 - GeneveOverhead = 50 - GREOverhead = 38 + VXLANOverhead = 50 + GeneveOverhead = 50 + GREOverhead = 38 + WireGuardOverhead = 80 // IPsec ESP can add a maximum of 38 bytes to the packet including the ESP // header and trailer. IPSecESPOverhead = 38 @@ -68,6 +69,17 @@ type AdapterNetConfig struct { Routes []interface{} } +type WireGuardConfig struct { + // Name is the name of WireGurad interface. e.g. antrea-wg0. + Name string + // LinkIndex is the link index of WireGuard interface. + LinkIndex int + // WireGuardPort is the port for the WireGuard to receive traffic. + WireGuardPort int + // The MTU of WireGuard interface. + MTU int +} + // Local Node configurations retrieved from K8s API or host networking state. type NodeConfig struct { // The Node's name used in Kubernetes. @@ -87,6 +99,8 @@ type NodeConfig struct { NodeIPAddr *net.IPNet // The IP on the Node's transport interface. It is used for tunneling or routing the Pod traffic across Nodes. NodeTransportIPAddr *net.IPNet + // The original MTU of Node's local interface which has the K8s Node IP. + NodeLocalInterfaceMTU int // Set either via defaultMTU config in antrea.yaml or auto discovered. // Auto discovery will use MTU value of the Node's primary interface. // For Encap and Hybrid mode, Node MTU will be adjusted to account for encap header. @@ -95,6 +109,8 @@ type NodeConfig struct { GatewayConfig *GatewayConfig // The config of the OVS bridge uplink interface. Only for Windows Node. UplinkNetConfig *AdapterNetConfig + // The config of the WireGuard interface. + WireGuardConfig *WireGuardConfig } func (n *NodeConfig) String() string { @@ -104,11 +120,11 @@ func (n *NodeConfig) String() string { // User provided network configuration parameters. type NetworkConfig struct { - TrafficEncapMode TrafficEncapModeType - TunnelType ovsconfig.TunnelType - EnableIPSecTunnel bool - IPSecPSK string - TransportIface string + TrafficEncapMode TrafficEncapModeType + TunnelType ovsconfig.TunnelType + TrafficEncryptionMode TrafficEncryptionModeType + IPSecPSK string + TransportIface string } // IsIPv4Enabled returns true if the cluster network supports IPv4. diff --git a/pkg/agent/config/traffic_encryption_mode.go b/pkg/agent/config/traffic_encryption_mode.go new file mode 100644 index 00000000000..e21d445a1eb --- /dev/null +++ b/pkg/agent/config/traffic_encryption_mode.go @@ -0,0 +1,60 @@ +// Copyright 2021 Antrea 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 config + +import ( + "strings" +) + +type TrafficEncryptionModeType int + +const ( + TrafficEncryptionModeNone TrafficEncryptionModeType = iota + TrafficEncryptionModeIPSec + TrafficEncryptionModeWireGuard + TrafficEncryptionModeInvalid = -1 +) + +var ( + encryptionModeStrs = [...]string{ + "None", + "IPsec", + "WireGuard", + } +) + +// GetTrafficEncryptionModeFromStr returns true and TrafficEncryptionModeType corresponding to input string. +// Otherwise, false and undefined value is returned +func GetTrafficEncryptionModeFromStr(str string) (bool, TrafficEncryptionModeType) { + for idx, ms := range encryptionModeStrs { + if strings.EqualFold(ms, str) { + return true, TrafficEncryptionModeType(idx) + } + } + return false, TrafficEncryptionModeInvalid +} + +func GetTrafficEncryptionModes() []TrafficEncryptionModeType { + return []TrafficEncryptionModeType{ + TrafficEncryptionModeNone, + TrafficEncryptionModeIPSec, + TrafficEncryptionModeWireGuard, + } +} + +// String returns value in string. +func (m TrafficEncryptionModeType) String() string { + return encryptionModeStrs[m] +} diff --git a/pkg/agent/config/traffic_encryption_mode_test.go b/pkg/agent/config/traffic_encryption_mode_test.go new file mode 100644 index 00000000000..3aebda6cfc5 --- /dev/null +++ b/pkg/agent/config/traffic_encryption_mode_test.go @@ -0,0 +1,66 @@ +// Copyright 2021 Antrea 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 config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetTrafficEncryptionModes(t *testing.T) { + modes := GetTrafficEncryptionModes() + expModes := []TrafficEncryptionModeType{0, 1, 2} + assert.Equal(t, expModes, modes, "TestGetTrafficEncryptionModes received unexpected encryption modes") +} + +func TestGetTrafficEncryptionModeFromStr(t *testing.T) { + tests := []struct { + name string + input string + expBool bool + expMode TrafficEncryptionModeType + }{ + {"None by default", "none", true, TrafficEncryptionModeNone}, + {"IPsec", "ipsec", true, TrafficEncryptionModeIPSec}, + {"WireGuard", "wireguard", true, TrafficEncryptionModeWireGuard}, + {"Capital case", "IPsec", true, TrafficEncryptionModeIPSec}, + {"Invalid string", "wire guard", false, TrafficEncryptionModeInvalid}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ok, mode := GetTrafficEncryptionModeFromStr(tt.input) + assert.Equal(t, tt.expBool, ok, "GetTrafficEncryptionModeFromStr did not return correct boolean") + assert.Equal(t, tt.expMode, mode, "GetTrafficEncryptionModeFromStr did not return correct string") + }) + } +} + +func TestTrafficEncryptionModeType_String(t *testing.T) { + tests := []struct { + name string + m TrafficEncryptionModeType + want string + }{ + {"None", TrafficEncryptionModeNone, "None"}, + {"IPsec", TrafficEncryptionModeIPSec, "IPsec"}, + {"WireGuard", TrafficEncryptionModeWireGuard, "WireGuard"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.m.String(), "TrafficEncryptionModeType.String did not return correct string representation") + }) + } +} diff --git a/pkg/agent/controller/noderoute/node_route_controller.go b/pkg/agent/controller/noderoute/node_route_controller.go index 8480395984e..c8828ea82ba 100644 --- a/pkg/agent/controller/noderoute/node_route_controller.go +++ b/pkg/agent/controller/noderoute/node_route_controller.go @@ -37,6 +37,7 @@ import ( "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" "antrea.io/antrea/pkg/agent/util" + "antrea.io/antrea/pkg/agent/wireguard" "antrea.io/antrea/pkg/ovs/ovsconfig" utilip "antrea.io/antrea/pkg/util/ip" "antrea.io/antrea/pkg/util/k8s" @@ -73,7 +74,8 @@ type Controller struct { // installedNodes records routes and flows installation states of Nodes. // The key is the host name of the Node, the value is the nodeRouteInfo of the Node. // A node will be in the map after its flows and routes are installed successfully. - installedNodes cache.Indexer + installedNodes cache.Indexer + wireGuardClient wireguard.Interface } // NewNodeRouteController instantiates a new Controller object which will process Node events @@ -86,7 +88,9 @@ func NewNodeRouteController( routeClient route.Interface, interfaceStore interfacestore.InterfaceStore, networkConfig *config.NetworkConfig, - nodeConfig *config.NodeConfig) *Controller { + nodeConfig *config.NodeConfig, + wireguardClient wireguard.Interface, +) *Controller { nodeInformer := informerFactory.Core().V1().Nodes() controller := &Controller{ kubeClient: kubeClient, @@ -101,6 +105,7 @@ func NewNodeRouteController( nodeListerSynced: nodeInformer.Informer().HasSynced, queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "noderoute"), installedNodes: cache.NewIndexer(nodeRouteInfoKeyFunc, cache.Indexers{nodeRouteInfoPodCIDRIndexName: nodeRouteInfoPodCIDRIndexFunc}), + wireGuardClient: wireguardClient, } nodeInformer.Informer().AddEventHandlerWithResyncPeriod( cache.ResourceEventHandlerFuncs{ @@ -133,11 +138,12 @@ func nodeRouteInfoPodCIDRIndexFunc(obj interface{}) ([]string, error) { // nodeRouteInfo is the route related information extracted from corev1.Node. type nodeRouteInfo struct { - nodeName string - podCIDRs []*net.IPNet - nodeIP net.IP - gatewayIP []net.IP - nodeMAC net.HardwareAddr + nodeName string + podCIDRs []*net.IPNet + nodeIP net.IP + gatewayIP []net.IP + nodeMAC net.HardwareAddr + wireGuardPublicKey string } // enqueueNode adds an object to the controller work queue @@ -207,7 +213,7 @@ func (c *Controller) removeStaleTunnelPorts() error { // knownInterfaces is the list of interfaces currently in the local cache. knownInterfaces := c.interfaceStore.GetInterfaceKeysByType(interfacestore.TunnelInterface) - if c.networkConfig.EnableIPSecTunnel { + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeIPSec { for _, node := range nodes { interfaceConfig, found := c.interfaceStore.GetNodeTunnelInterface(node.Name) if !found { @@ -275,9 +281,30 @@ func (c *Controller) reconcile() error { if err := c.removeStaleTunnelPorts(); err != nil { return fmt.Errorf("error when removing stale tunnel ports: %v", err) } + if err := c.removeStaleWireGuardPeers(); err != nil { + return fmt.Errorf("error when removing stale WireGuard peers: %v", err) + } return nil } +// removeStaleWireGuardPeers deletes stale WireGuard peers if necessary. +func (c *Controller) removeStaleWireGuardPeers() error { + if c.networkConfig.TrafficEncryptionMode != config.TrafficEncryptionModeWireGuard { + return nil + } + nodes, err := c.nodeLister.List(labels.Everything()) + if err != nil { + return fmt.Errorf("error when listing Nodes: %v", err) + } + var currentPeerPublicKeys []string + for _, n := range nodes { + if pubkey, ok := n.Annotations[types.NodeWireGuardPublicKey]; ok { + currentPeerPublicKeys = append(currentPeerPublicKeys, pubkey) + } + } + return c.wireGuardClient.RemoveStalePeers(currentPeerPublicKeys) +} + // Run will create defaultWorkers workers (go routines) which will process the Node events from the // workqueue. func (c *Controller) Run(stopCh <-chan struct{}) { @@ -299,7 +326,7 @@ func (c *Controller) Run(stopCh <-chan struct{}) { } if err := c.reconcile(); err != nil { - klog.Errorf("Error during %s reconciliation", controllerName) + klog.ErrorS(err, "Error during reconciliation", "controller", controllerName) } for i := 0; i < defaultWorkers; i++ { @@ -398,7 +425,7 @@ func (c *Controller) deleteNodeRoute(nodeName string) error { } c.installedNodes.Delete(obj) - if c.networkConfig.EnableIPSecTunnel { + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeIPSec { interfaceConfig, ok := c.interfaceStore.GetNodeTunnelInterface(nodeName) if !ok { // Tunnel port not created for this Node. @@ -411,6 +438,12 @@ func (c *Controller) deleteNodeRoute(nodeName string) error { } c.interfaceStore.DeleteInterface(interfaceConfig) } + + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeWireGuard { + if err := c.wireGuardClient.DeletePeer(nodeName); err != nil { + return fmt.Errorf("delete WireGuard peer %s failed: %v", nodeName, err) + } + } return nil } @@ -424,11 +457,14 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { if err != nil { return err } + peerWireGuardPublicKey := node.Annotations[types.NodeWireGuardPublicKey] nrInfo, installed, _ := c.installedNodes.GetByKey(nodeName) - - if installed && nrInfo.(*nodeRouteInfo).nodeMAC.String() == peerNodeMAC.String() && nrInfo.(*nodeRouteInfo).nodeIP.Equal(peerNodeIP) { - // Route is already added for this Node and both Node MAC and transport IP are not changed. + if installed && nrInfo.(*nodeRouteInfo).nodeMAC.String() == peerNodeMAC.String() && + nrInfo.(*nodeRouteInfo).nodeIP.Equal(peerNodeIP) && + nrInfo.(*nodeRouteInfo).wireGuardPublicKey == peerWireGuardPublicKey { + // Route is already added for this Node and Node MAC, transport IP + // and WireGuard public key are not changed. return nil } @@ -481,7 +517,7 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { } ipsecTunOFPort := int32(0) - if c.networkConfig.EnableIPSecTunnel { + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeIPSec { // Create a separate tunnel port for the Node, as OVS IPSec monitor needs to // read PSK and remote IP from the Node's tunnel interface to create IPSec // security policies. @@ -490,6 +526,12 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { } } + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeWireGuard && peerWireGuardPublicKey != "" { + if err := c.wireGuardClient.UpdatePeer(nodeName, peerWireGuardPublicKey, peerNodeIP, podCIDRs); err != nil { + return err + } + } + err = c.ofClient.InstallNodeFlows( nodeName, peerConfig, @@ -507,12 +549,14 @@ func (c *Controller) addNodeRoute(nodeName string, node *corev1.Node) error { } peerGatewayIPs = append(peerGatewayIPs, peerGatewayIP) } + c.installedNodes.Add(&nodeRouteInfo{ - nodeName: nodeName, - podCIDRs: podCIDRs, - nodeIP: peerNodeIP, - gatewayIP: peerGatewayIPs, - nodeMAC: peerNodeMAC, + nodeName: nodeName, + podCIDRs: podCIDRs, + nodeIP: peerNodeIP, + gatewayIP: peerGatewayIPs, + nodeMAC: peerNodeMAC, + wireGuardPublicKey: peerWireGuardPublicKey, }) return err } diff --git a/pkg/agent/controller/noderoute/node_route_controller_test.go b/pkg/agent/controller/noderoute/node_route_controller_test.go index 893ea50a96c..1ee79970978 100644 --- a/pkg/agent/controller/noderoute/node_route_controller_test.go +++ b/pkg/agent/controller/noderoute/node_route_controller_test.go @@ -69,7 +69,7 @@ func newController(t *testing.T, networkConfig *config.NetworkConfig) (*fakeCont c := NewNodeRouteController(clientset, informerFactory, ofClient, ovsClient, routeClient, interfaceStore, networkConfig, &config.NodeConfig{GatewayConfig: &config.GatewayConfig{ IPv4: nil, MAC: gatewayMAC, - }}) + }}, nil) return &fakeController{ Controller: c, clientset: clientset, @@ -234,10 +234,10 @@ func TestIPInPodSubnets(t *testing.T) { func setup(t *testing.T, ifaces []*interfacestore.InterfaceConfig) (*fakeController, func()) { c, closeFn := newController(t, &config.NetworkConfig{ - TrafficEncapMode: 0, - TunnelType: ovsconfig.TunnelType("vxlan"), - EnableIPSecTunnel: true, - IPSecPSK: "changeme", + TrafficEncapMode: 0, + TunnelType: ovsconfig.TunnelType("vxlan"), + TrafficEncryptionMode: config.TrafficEncryptionModeIPSec, + IPSecPSK: "changeme", }) for _, i := range ifaces { c.interfaceStore.AddInterface(i) diff --git a/pkg/agent/openflow/client.go b/pkg/agent/openflow/client.go index 9500e4598b8..e2544f17844 100644 --- a/pkg/agent/openflow/client.go +++ b/pkg/agent/openflow/client.go @@ -39,7 +39,7 @@ type Client interface { // be called to ensure that the set of OVS flows is correct. All flows programmed in the // switch which match the current round number will be deleted before any new flow is // installed. - Initialize(roundInfo types.RoundInfo, config *config.NodeConfig, encapMode config.TrafficEncapModeType) (<-chan struct{}, error) + Initialize(roundInfo types.RoundInfo, config *config.NodeConfig, networkconfig *config.NetworkConfig) (<-chan struct{}, error) // InstallGatewayFlows sets up flows related to an OVS gateway port, the gateway must exist. InstallGatewayFlows() error @@ -416,20 +416,21 @@ func (c *client) InstallNodeFlows(hostname string, peerConfigs map[*net.IPNet]net.IP, tunnelPeerIP net.IP, ipsecTunOFPort uint32, - remoteGatewayMAC net.HardwareAddr) error { + remoteGatewayMAC net.HardwareAddr, +) error { c.replayMutex.RLock() defer c.replayMutex.RUnlock() var flows []binding.Flow localGatewayMAC := c.nodeConfig.GatewayConfig.MAC - for peerPodCIDR, peerGatewayIP := range peerConfigs { if peerGatewayIP.To4() != nil { // Since broadcast is not supported in IPv6, ARP should happen only with IPv4 address, and ARP responder flows // only work for IPv4 addresses. flows = append(flows, c.arpResponderFlow(peerGatewayIP, cookie.Node)) } - if c.encapMode.NeedsEncapToPeer(tunnelPeerIP, c.nodeConfig.NodeTransportIPAddr) { + // If WireGuard is enabled, traffic does not need to be encapsulated from OVS side. + if c.networkConfig.TrafficEncryptionMode != config.TrafficEncryptionModeWireGuard && c.networkConfig.TrafficEncapMode.NeedsEncapToPeer(tunnelPeerIP, c.nodeConfig.NodeTransportIPAddr) { // tunnelPeerIP is the Node Internal Address. In a dual-stack setup, whether this address is an IPv4 address or an // IPv6 one is decided by the address family of Node Internal Address. flows = append(flows, c.l3FwdFlowToRemote(localGatewayMAC, *peerPodCIDR, tunnelPeerIP, cookie.Node)) @@ -476,7 +477,7 @@ func (c *client) InstallPodFlows(interfaceName string, podInterfaceIPs []net.IP, // Add L3 Routing flows to rewrite Pod's dst MAC for all validate IPs. flows = append(flows, c.l3FwdFlowToPod(localGatewayMAC, podInterfaceIPs, podInterfaceMAC, cookie.Pod)...) - if c.encapMode.IsNetworkPolicyOnly() { + if c.networkConfig.TrafficEncapMode.IsNetworkPolicyOnly() { // In policy-only mode, traffic to local Pod is routed based on destination IP. flows = append(flows, c.l3FwdFlowRouteToPod(podInterfaceIPs, podInterfaceMAC, cookie.Pod)..., @@ -708,7 +709,7 @@ func (c *client) initialize() error { if err := c.ofEntryOperations.AddAll(c.rejectBypassNetworkpolicyFlows(cookie.Default)); err != nil { return fmt.Errorf("failed to install flows to skip generated reject responses: %v", err) } - if c.encapMode.IsNetworkPolicyOnly() { + if c.networkConfig.TrafficEncapMode.IsNetworkPolicyOnly() { if err := c.setupPolicyOnlyFlows(); err != nil { return fmt.Errorf("failed to setup policy only flows: %w", err) } @@ -724,14 +725,14 @@ func (c *client) initialize() error { return nil } -func (c *client) Initialize(roundInfo types.RoundInfo, nodeConfig *config.NodeConfig, encapMode config.TrafficEncapModeType) (<-chan struct{}, error) { +func (c *client) Initialize(roundInfo types.RoundInfo, nodeConfig *config.NodeConfig, networkConfig *config.NetworkConfig) (<-chan struct{}, error) { c.nodeConfig = nodeConfig - c.encapMode = encapMode + c.networkConfig = networkConfig - if config.IsIPv4Enabled(nodeConfig, encapMode) { + if config.IsIPv4Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) { c.ipProtocols = append(c.ipProtocols, binding.ProtocolIP) } - if config.IsIPv6Enabled(nodeConfig, encapMode) { + if config.IsIPv6Enabled(nodeConfig, c.networkConfig.TrafficEncapMode) { c.ipProtocols = append(c.ipProtocols, binding.ProtocolIPv6) } @@ -990,11 +991,11 @@ func (c *client) InitialTLVMap() error { } func (c *client) IsIPv4Enabled() bool { - return config.IsIPv4Enabled(c.nodeConfig, c.encapMode) + return config.IsIPv4Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) } func (c *client) IsIPv6Enabled() bool { - return config.IsIPv6Enabled(c.nodeConfig, c.encapMode) + return config.IsIPv6Enabled(c.nodeConfig, c.networkConfig.TrafficEncapMode) } // setBasePacketOutBuilder sets base IP properties of a packetOutBuilder which can have more packet data added. diff --git a/pkg/agent/openflow/client_test.go b/pkg/agent/openflow/client_test.go index d2e4f3edd57..2d197847fdc 100644 --- a/pkg/agent/openflow/client_test.go +++ b/pkg/agent/openflow/client_test.go @@ -48,7 +48,11 @@ var ( IPv6: gwIPv6, MAC: gwMAC, } - nodeConfig = &config.NodeConfig{GatewayConfig: gatewayConfig} + nodeConfig = &config.NodeConfig{ + GatewayConfig: gatewayConfig, + WireGuardConfig: &config.WireGuardConfig{}, + } + networkConig = &config.NetworkConfig{} ) func installNodeFlows(ofClient Client, cacheKey string) (int, error) { @@ -103,6 +107,7 @@ func TestIdempotentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig m.EXPECT().AddAll(gomock.Any()).Return(nil).Times(1) // Installing the flows should succeed, and all the flows should be added into the cache. @@ -131,6 +136,7 @@ func TestIdempotentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig errorCall := m.EXPECT().AddAll(gomock.Any()).Return(errors.New("Bundle error")).Times(1) m.EXPECT().AddAll(gomock.Any()).Return(nil).After(errorCall) @@ -172,6 +178,7 @@ func TestFlowInstallationFailed(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig // We generate an error for AddAll call. m.EXPECT().AddAll(gomock.Any()).Return(errors.New("Bundle error")) @@ -206,6 +213,7 @@ func TestConcurrentFlowInstallation(t *testing.T) { client.cookieAllocator = cookie.NewAllocator(0) client.ofEntryOperations = m client.nodeConfig = nodeConfig + client.networkConfig = networkConig var concurrentCalls atomic.Value // set to true if we observe concurrent calls timeoutCh := make(chan struct{}) diff --git a/pkg/agent/openflow/network_policy_test.go b/pkg/agent/openflow/network_policy_test.go index 86bbb6916c8..efdffd06602 100644 --- a/pkg/agent/openflow/network_policy_test.go +++ b/pkg/agent/openflow/network_policy_test.go @@ -156,6 +156,7 @@ func TestInstallPolicyRuleFlows(t *testing.T) { c = prepareClient(ctrl) c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP} defaultAction := crdv1alpha1.RuleActionAllow ruleID1 := uint32(101) @@ -511,6 +512,7 @@ func TestBatchInstallPolicyRuleFlows(t *testing.T) { c.cookieAllocator = cookie.NewAllocator(0) c.ofEntryOperations = mockOperations c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: nil} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP} c.deterministic = true @@ -636,6 +638,7 @@ func TestInstallPolicyRuleFlowsInDualStackCluster(t *testing.T) { c = prepareClient(ctrl) c.nodeConfig = &config.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR} + c.networkConfig = &config.NetworkConfig{} c.ipProtocols = []binding.Protocol{binding.ProtocolIP, binding.ProtocolIPv6} defaultAction := crdv1alpha1.RuleActionAllow ruleID1 := uint32(101) diff --git a/pkg/agent/openflow/pipeline.go b/pkg/agent/openflow/pipeline.go index 187e30f9bf9..7adf2cc0a77 100644 --- a/pkg/agent/openflow/pipeline.go +++ b/pkg/agent/openflow/pipeline.go @@ -295,6 +295,7 @@ type client struct { enableAntreaPolicy bool enableDenyTracking bool enableEgress bool + enableWireGuard bool roundInfo types.RoundInfo cookieAllocator cookie.Allocator bridge binding.Bridge @@ -320,7 +321,7 @@ type client struct { // replayMutex provides exclusive access to the OFSwitch to the ReplayFlows method. replayMutex sync.RWMutex nodeConfig *config.NodeConfig - encapMode config.TrafficEncapModeType + networkConfig *config.NetworkConfig gatewayOFPort uint32 // ovsDatapathType is the type of the datapath used by the bridge. ovsDatapathType ovsconfig.OVSDatapathType @@ -876,7 +877,7 @@ func (c *client) traceflowL2ForwardOutputFlows(dataplaneTag uint8, liveTraffic, flows := []binding.Flow{} l2FwdOutTable := c.pipeline[L2ForwardingOutTable] for _, ipProtocol := range c.ipProtocols { - if c.encapMode.SupportsEncap() { + if c.networkConfig.TrafficEncapMode.SupportsEncap() { // SendToController and Output if output port is tunnel port. fb1 := l2FwdOutTable.BuildFlow(priorityNormal+3). MatchRegFieldWithValue(TargetOFPortField, config.DefaultTunOFPort). diff --git a/pkg/agent/openflow/pipeline_windows.go b/pkg/agent/openflow/pipeline_windows.go index a31e552526b..e4b366c2577 100644 --- a/pkg/agent/openflow/pipeline_windows.go +++ b/pkg/agent/openflow/pipeline_windows.go @@ -93,7 +93,7 @@ func (c *client) hostBridgeUplinkFlows(localSubnet net.IPNet, category cookie.Ca Cookie(c.cookieAllocator.Request(category).Raw()). Done(), } - if c.encapMode.SupportsNoEncap() { + if c.networkConfig.TrafficEncapMode.SupportsNoEncap() { // If NoEncap is enabled, the reply packets from remote Pod can be forwarded to local Pod directly. // by explicitly resubmitting them to serviceHairpinTable and marking "macRewriteMark" at same time. flows = append(flows, c.pipeline[ClassifierTable].BuildFlow(priorityHigh).MatchProtocol(binding.ProtocolIP). @@ -110,7 +110,7 @@ func (c *client) hostBridgeUplinkFlows(localSubnet net.IPNet, category cookie.Ca func (c *client) l3FwdFlowToRemoteViaRouting(localGatewayMAC net.HardwareAddr, remoteGatewayMAC net.HardwareAddr, category cookie.Category, peerIP net.IP, peerPodCIDR *net.IPNet) []binding.Flow { - if c.encapMode.NeedsDirectRoutingToPeer(peerIP, c.nodeConfig.NodeTransportIPAddr) && remoteGatewayMAC != nil { + if c.networkConfig.TrafficEncapMode.NeedsDirectRoutingToPeer(peerIP, c.nodeConfig.NodeTransportIPAddr) && remoteGatewayMAC != nil { ipProto := getIPProtocol(peerIP) l3FwdTable := c.pipeline[l3ForwardingTable] // It enhances Windows Noencap mode performance by bypassing host network. diff --git a/pkg/agent/openflow/testing/mock_openflow.go b/pkg/agent/openflow/testing/mock_openflow.go index e12335b627e..ff69b4e96d2 100644 --- a/pkg/agent/openflow/testing/mock_openflow.go +++ b/pkg/agent/openflow/testing/mock_openflow.go @@ -250,7 +250,7 @@ func (mr *MockClientMockRecorder) InitialTLVMap() *gomock.Call { } // Initialize mocks base method -func (m *MockClient) Initialize(arg0 types.RoundInfo, arg1 *config.NodeConfig, arg2 config.TrafficEncapModeType) (<-chan struct{}, error) { +func (m *MockClient) Initialize(arg0 types.RoundInfo, arg1 *config.NodeConfig, arg2 *config.NetworkConfig) (<-chan struct{}, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Initialize", arg0, arg1, arg2) ret0, _ := ret[0].(<-chan struct{}) diff --git a/pkg/agent/route/route_linux.go b/pkg/agent/route/route_linux.go index 5327e7fcaf1..79383129ef8 100644 --- a/pkg/agent/route/route_linux.go +++ b/pkg/agent/route/route_linux.go @@ -577,7 +577,16 @@ func (c *Client) AddRoutes(podCIDR *net.IPNet, nodeName string, nodeIP, nodeGwIP Dst: podCIDR, } var routes []*netlink.Route - if c.networkConfig.TrafficEncapMode.NeedsEncapToPeer(nodeIP, c.nodeConfig.NodeTransportIPAddr) { + // If WireGuard is enabled, create a route via WireGuard device regardless of the traffic encapsulation modes. + if c.networkConfig.TrafficEncryptionMode == config.TrafficEncryptionModeWireGuard { + route.LinkIndex = c.nodeConfig.WireGuardConfig.LinkIndex + route.Scope = netlink.SCOPE_LINK + if podCIDR.IP.To4() != nil { + route.Src = c.nodeConfig.GatewayConfig.IPv4 + } else { + route.Src = c.nodeConfig.GatewayConfig.IPv6 + } + } else if c.networkConfig.TrafficEncapMode.NeedsEncapToPeer(nodeIP, c.nodeConfig.NodeTransportIPAddr) { if podCIDR.IP.To4() == nil { // "on-link" is not identified in IPv6 route entries, so split the configuration into 2 entries. routes = []*netlink.Route{ diff --git a/pkg/agent/types/annotations.go b/pkg/agent/types/annotations.go index 4353eb11d9b..2ecff70a1f3 100644 --- a/pkg/agent/types/annotations.go +++ b/pkg/agent/types/annotations.go @@ -17,7 +17,8 @@ package types const ( // NodeMACAddressAnnotationKey represents the key of the Node's MAC address in the Annotations of the Node. NodeMACAddressAnnotationKey string = "node.antrea.io/mac-address" - // NodeTransportAddressAnnotationKey represents the key of the interface's IP address on which the Node transfers Pod traffic in the Annotations of the Node. NodeTransportAddressAnnotationKey string = "node.antrea.io/transport-address" + // NodeWireGuardPublicKey represents the key of the Node's WireGuard public key in the Annotations of the Node. + NodeWireGuardPublicKey string = "node.antrea.io/wireguard-publickey" ) diff --git a/pkg/agent/wireguard/client_linux.go b/pkg/agent/wireguard/client_linux.go new file mode 100644 index 00000000000..5ee503137c8 --- /dev/null +++ b/pkg/agent/wireguard/client_linux.go @@ -0,0 +1,240 @@ +// +build linux + +// Copyright 2021 Antrea 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 wireguard + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "strconv" + "sync" + + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apitypes "k8s.io/apimachinery/pkg/types" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" + "k8s.io/klog/v2" + + "antrea.io/antrea/pkg/agent/config" + "antrea.io/antrea/pkg/agent/types" +) + +var zeroKey = wgtypes.Key{} + +// wgctrlClient is an interface to mock wgctrl.Client +type wgctrlClient interface { + io.Closer + Devices() ([]*wgtypes.Device, error) + Device(name string) (*wgtypes.Device, error) + ConfigureDevice(name string, config wgtypes.Config) error +} + +var _ Interface = (*client)(nil) + +type client struct { + wgClient wgctrlClient + nodeName string + k8sClient clientset.Interface + privateKey wgtypes.Key + peerPublicKeyByNodeName *sync.Map + nodeIP net.IP + wireGuardConfig *config.WireGuardConfig +} + +func New(clientSet clientset.Interface, nodeConfig *config.NodeConfig, wireGuardConfig *config.WireGuardConfig) (Interface, error) { + wgClient, err := wgctrl.New() + if err != nil { + return nil, err + } + c := &client{ + wgClient: wgClient, + nodeName: nodeConfig.Name, + k8sClient: clientSet, + nodeIP: nodeConfig.NodeIPAddr.IP, + wireGuardConfig: wireGuardConfig, + peerPublicKeyByNodeName: &sync.Map{}, + } + return c, nil +} + +func (client *client) Init() error { + link := &netlink.Wireguard{LinkAttrs: netlink.LinkAttrs{Name: client.wireGuardConfig.Name, MTU: client.wireGuardConfig.MTU}} + err := netlink.LinkAdd(link) + // ignore existing link as it may have already been created or managed by userspace process. + if err != nil && !errors.Is(err, unix.EEXIST) { + if errors.Is(err, unix.EOPNOTSUPP) { + return fmt.Errorf("WireGuard not supported by the Linux kernel (netlink: %w). Make sure WireGuard kernel module is loaded", err) + } + return err + } + if err := netlink.LinkSetUp(link); err != nil { + return err + } + client.wireGuardConfig.LinkIndex = link.Attrs().Index + wgDev, err := client.wgClient.Device(client.wireGuardConfig.Name) + if err != nil { + return err + } + client.privateKey = wgDev.PrivateKey + // WireGuard private key will be persistent when agent restarts. So antrea only needs to + // generate a new private key if it is empty (all zero). + if client.privateKey == zeroKey { + newPkey, err := wgtypes.GeneratePrivateKey() + if err != nil { + return err + } + client.privateKey = newPkey + } + cfg := wgtypes.Config{ + PrivateKey: &client.privateKey, + ListenPort: &client.wireGuardConfig.WireGuardPort, + ReplacePeers: false, + } + patch, _ := json.Marshal(map[string]interface{}{ + "metadata": map[string]interface{}{ + "annotations": map[string]string{ + types.NodeWireGuardPublicKey: client.privateKey.PublicKey().String(), + }, + }, + }) + if err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + _, err := client.k8sClient.CoreV1().Nodes().Patch(context.TODO(), client.nodeName, apitypes.MergePatchType, patch, metav1.PatchOptions{}) + return err + }); err != nil { + return err + } + + return client.wgClient.ConfigureDevice(client.wireGuardConfig.Name, cfg) +} + +func (client *client) RemoveStalePeers(existingPeerPublickeys []string) error { + wgdev, err := client.wgClient.Device(client.wireGuardConfig.Name) + if err != nil { + return err + } + restoredPeerPublicKeys := make(map[wgtypes.Key]struct{}) + for _, peer := range wgdev.Peers { + restoredPeerPublicKeys[peer.PublicKey] = struct{}{} + } + for _, e := range existingPeerPublickeys { + pubKey, err := wgtypes.ParseKey(e) + if err != nil { + klog.ErrorS(err, "Parse WireGuard public error") + continue + } + delete(restoredPeerPublicKeys, pubKey) + } + for k := range restoredPeerPublicKeys { + if err := client.deletePeerByPublicKey(k); err != nil { + klog.ErrorS(err, "Delete WireGuard peer error") + } + } + return nil +} + +func (client *client) UpdatePeer(nodeName, publicKeyString string, peerNodeIP net.IP, podCIDRs []*net.IPNet) error { + pubKey, err := wgtypes.ParseKey(publicKeyString) + if err != nil { + return err + } + var allowedIPs []net.IPNet + peerNodeIPv4, peerNodeIPv6 := peerNodeIP.To4(), peerNodeIP.To16() + + if peerNodeIPv4 != nil { + allowedIPs = append(allowedIPs, net.IPNet{ + IP: peerNodeIPv4, + Mask: net.CIDRMask(net.IPv4len*8, net.IPv4len*8), + }) + } else if peerNodeIPv6 != nil { + allowedIPs = append(allowedIPs, net.IPNet{ + IP: peerNodeIPv6, + Mask: net.CIDRMask(net.IPv6len*8, net.IPv6len*8), + }) + } else { + return fmt.Errorf("peer Node IP is not valid: %s", peerNodeIP.String()) + } + + for _, cidr := range podCIDRs { + allowedIPs = append(allowedIPs, *cidr) + } + + if key, exist := client.peerPublicKeyByNodeName.Load(nodeName); exist { + cachedPeerPubKey := key.(wgtypes.Key) + if cachedPeerPubKey != pubKey { + klog.InfoS("WireGuard peer public key updated", "nodeName", nodeName, "publicKey", publicKeyString) + // delete old peer by public key. + if err := client.deletePeerByPublicKey(cachedPeerPubKey); err != nil { + return err + } + } + } + var peerNodeIPstring string + if peerNodeIPv4 != nil { + peerNodeIPstring = peerNodeIPv4.String() + } else { + peerNodeIPstring = peerNodeIPv6.String() + } + endpoint := net.JoinHostPort(peerNodeIPstring, strconv.Itoa(client.wireGuardConfig.WireGuardPort)) + endpointUDP, err := net.ResolveUDPAddr("udp", endpoint) + if err != nil { + return err + } + client.peerPublicKeyByNodeName.Store(nodeName, pubKey) + peerConfig := wgtypes.PeerConfig{ + PublicKey: pubKey, + Endpoint: endpointUDP, + AllowedIPs: allowedIPs, + ReplaceAllowedIPs: true, + } + cfg := wgtypes.Config{ + ReplacePeers: false, + Peers: []wgtypes.PeerConfig{peerConfig}, + } + return client.wgClient.ConfigureDevice(client.wireGuardConfig.Name, cfg) +} + +func (client *client) deletePeerByPublicKey(pubKey wgtypes.Key) error { + cfg := wgtypes.Config{Peers: []wgtypes.PeerConfig{ + {PublicKey: pubKey, Remove: true}, + }} + return client.wgClient.ConfigureDevice(client.wireGuardConfig.Name, cfg) +} + +func (client *client) DeletePeer(nodeName string) error { + key, exist := client.peerPublicKeyByNodeName.Load(nodeName) + if !exist { + return nil + } + peerPublicKey := key.(wgtypes.Key) + if err := client.deletePeerByPublicKey(peerPublicKey); err != nil { + return err + } + client.peerPublicKeyByNodeName.Delete(nodeName) + return nil +} + +func (client *client) PeerConfigured(nodeName string) bool { + _, exist := client.peerPublicKeyByNodeName.Load(nodeName) + return exist +} diff --git a/pkg/agent/wireguard/client_test.go b/pkg/agent/wireguard/client_test.go new file mode 100644 index 00000000000..a0d11850e81 --- /dev/null +++ b/pkg/agent/wireguard/client_test.go @@ -0,0 +1,398 @@ +// +build linux + +// Copyright 2021 Antrea 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 wireguard + +import ( + "net" + "sync" + "testing" + + "github.com/stretchr/testify/assert" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + + "antrea.io/antrea/pkg/agent/config" +) + +type fakeWireGuardClient struct { + peers map[wgtypes.Key]wgtypes.Peer +} + +func (f *fakeWireGuardClient) Close() error { + return nil +} + +func (f *fakeWireGuardClient) Devices() ([]*wgtypes.Device, error) { + return nil, nil +} + +func (f *fakeWireGuardClient) Device(name string) (*wgtypes.Device, error) { + var res []wgtypes.Peer + for _, p := range f.peers { + res = append(res, p) + } + return &wgtypes.Device{ + Peers: res, + }, nil +} + +func (f *fakeWireGuardClient) ConfigureDevice(name string, cfg wgtypes.Config) error { + for _, c := range cfg.Peers { + if c.Remove { + delete(f.peers, c.PublicKey) + } else { + peer := wgtypes.Peer{ + PublicKey: c.PublicKey, + Endpoint: c.Endpoint, + } + if c.Endpoint != nil { + ep := &net.UDPAddr{} + ip := c.Endpoint.IP + ep.IP = append(ip[:0:0], ip...) + ep.Port = c.Endpoint.Port + ep.Zone = c.Endpoint.Zone + peer.Endpoint = ep + } + for _, n := range c.AllowedIPs { + peer.AllowedIPs = append(peer.AllowedIPs, net.IPNet{ + IP: append(n.IP[:0:0], n.IP...), + Mask: append(n.Mask[:0:0], n.Mask...), + }) + } + f.peers[c.PublicKey] = peer + } + } + return nil +} + +func getFakeClient() *client { + return &client{ + wgClient: &fakeWireGuardClient{}, + nodeName: "fake-node-1", + wireGuardConfig: &config.WireGuardConfig{ + MTU: 1420, + WireGuardPort: 12345, + }, + peerPublicKeyByNodeName: &sync.Map{}, + nodeIP: net.ParseIP("10.20.30.41"), + } +} + +func deepCopyForWireGuardPeer(peer wgtypes.Peer) wgtypes.Peer { + c := wgtypes.Peer{} + c.AllowedIPs = make([]net.IPNet, len(peer.AllowedIPs)) + for idx := range peer.AllowedIPs { + ip := peer.AllowedIPs[idx].IP + mask := peer.AllowedIPs[idx].Mask + c.AllowedIPs[idx].IP = append(ip[:0:0], ip...) + c.AllowedIPs[idx].Mask = append(mask[:0:0], mask...) + } + if peer.Endpoint != nil { + ep := &net.UDPAddr{} + ip := peer.Endpoint.IP + ep.IP = append(ip[:0:0], ip...) + ep.Port = peer.Endpoint.Port + ep.Zone = peer.Endpoint.Zone + c.Endpoint = ep + } + c.PublicKey = peer.PublicKey + return c +} + +func Test_RemoveStalePeers(t *testing.T) { + pk1, _ := wgtypes.GeneratePrivateKey() + pk2, _ := wgtypes.GeneratePrivateKey() + pk3, _ := wgtypes.GeneratePrivateKey() + tests := []struct { + name string + existingPeers map[wgtypes.Key]wgtypes.Peer + inputPublicKeys []string + expectedPeers map[wgtypes.Key]wgtypes.Peer + }{ + { + "pass empty/nil slice should remove all existing peers", + map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + }, + nil, + map[wgtypes.Key]wgtypes.Peer{}, + }, + { + "args has no intersection with existing peers", + map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + }, + []string{pk3.PublicKey().String()}, + map[wgtypes.Key]wgtypes.Peer{}, + }, + { + "should only keep peers passed by args", + map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): {PublicKey: pk1.PublicKey()}, + pk2.PublicKey(): {PublicKey: pk2.PublicKey()}, + pk3.PublicKey(): {PublicKey: pk3.PublicKey()}, + }, + []string{pk3.PublicKey().String()}, + map[wgtypes.Key]wgtypes.Peer{ + pk3.PublicKey(): {PublicKey: pk3.PublicKey()}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client := getFakeClient() + fc := &fakeWireGuardClient{peers: tt.existingPeers} + client.wgClient = fc + err := client.RemoveStalePeers(tt.inputPublicKeys) + assert.Nil(t, err) + assert.Equal(t, tt.expectedPeers, fc.peers) + }) + } +} + +func Test_UpdatePeer(t *testing.T) { + pk1, _ := wgtypes.GeneratePrivateKey() + pk2, _ := wgtypes.GeneratePrivateKey() + ip1, ipnet1, _ := net.ParseCIDR("10.20.30.42/32") + ip2, ipnet2, _ := net.ParseCIDR("10.20.30.43/32") + _, podCIDR1, _ := net.ParseCIDR("172.16.1.0/24") + _, podCIDR2, _ := net.ParseCIDR("172.16.2.0/24") + listenPort := getFakeClient().wireGuardConfig.WireGuardPort + tests := []struct { + name string + existingPeers map[string]wgtypes.Peer + inputPeerNodeName string + inputPeerNodePublicKey string + inputPeerNodeIP net.IP + inputPodCIDRs []*net.IPNet + expectedError bool + expectedPeers map[wgtypes.Key]wgtypes.Peer + }{ + { + "call update peer to add new peers", + map[string]wgtypes.Peer{}, + "fake-node-2", + pk1.PublicKey().String(), + ip1, + []*net.IPNet{podCIDR1}, + false, + map[wgtypes.Key]wgtypes.Peer{ + pk1.PublicKey(): { + PublicKey: pk1.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + }, + { + "call update peer to update the public key of an existing peer", + map[string]wgtypes.Peer{ + "fake-node-2": { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + "fake-node-2", + pk2.PublicKey().String(), + ip1, + []*net.IPNet{podCIDR1}, + false, + map[wgtypes.Key]wgtypes.Peer{ + pk2.PublicKey(): { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + }, + { + "call update peer to update Node IP of an existing peer", + map[string]wgtypes.Peer{ + "fake-node-2": { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + "fake-node-2", + pk2.PublicKey().String(), + ip2, + []*net.IPNet{podCIDR1}, + false, + map[wgtypes.Key]wgtypes.Peer{ + pk2.PublicKey(): { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip2, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet2, *podCIDR1}, + }, + }, + }, + { + "call update peer to update Pod CIDR of an existing peer", + map[string]wgtypes.Peer{ + "fake-node-2": { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + "fake-node-2", + pk2.PublicKey().String(), + ip1, + []*net.IPNet{podCIDR2}, + false, + map[wgtypes.Key]wgtypes.Peer{ + pk2.PublicKey(): { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR2}, + }, + }, + }, + { + "call update peer with invalid public key", + map[string]wgtypes.Peer{ + "fake-node-2": { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + "fake-node-2", + "invalid key", + ip1, + []*net.IPNet{podCIDR1}, + true, + map[wgtypes.Key]wgtypes.Peer{ + pk2.PublicKey(): { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + }, + { + "call update peer with nil IP", + map[string]wgtypes.Peer{ + "fake-node-2": { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + "fake-node-2", + pk2.PublicKey().String(), + nil, + []*net.IPNet{podCIDR1}, + true, + map[wgtypes.Key]wgtypes.Peer{ + pk2.PublicKey(): { + PublicKey: pk2.PublicKey(), + Endpoint: &net.UDPAddr{ + IP: ip1, + Port: listenPort, + }, + AllowedIPs: []net.IPNet{*ipnet1, *podCIDR1}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client := getFakeClient() + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{}, + } + for _, ec := range tt.existingPeers { + fc.peers[ec.PublicKey] = deepCopyForWireGuardPeer(ec) + } + client.wgClient = fc + err := client.UpdatePeer(tt.inputPeerNodeName, tt.inputPeerNodePublicKey, tt.inputPeerNodeIP, tt.inputPodCIDRs) + if tt.expectedError { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + } + t.Logf("fc peers: %#v", fc.peers) + assert.Equal(t, tt.expectedPeers, fc.peers) + }) + } +} + +func Test_DeletePeer(t *testing.T) { + client := getFakeClient() + fc := &fakeWireGuardClient{ + peers: map[wgtypes.Key]wgtypes.Peer{}, + } + client.wgClient = fc + pk1, _ := wgtypes.GeneratePrivateKey() + ip1, _, _ := net.ParseCIDR("10.20.30.42/32") + t.Run("delete non-existing peer", func(tt *testing.T) { + err := client.UpdatePeer("fake-node-1", pk1.String(), ip1.To4(), nil) + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 1) + _, ok := client.peerPublicKeyByNodeName.Load("fake-node-1") + assert.True(t, ok) + err = client.DeletePeer("fake-node-2") + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 1) + _, ok = client.peerPublicKeyByNodeName.Load("fake-node-1") + assert.True(t, ok) + }) + + t.Run("delete existing peer", func(tt *testing.T) { + err := client.DeletePeer("fake-node-1") + assert.NoError(tt, err) + assert.Len(tt, fc.peers, 0) + _, ok := client.peerPublicKeyByNodeName.Load("fake-node-1") + assert.False(t, ok) + }) + +} diff --git a/pkg/agent/wireguard/client_windows.go b/pkg/agent/wireguard/client_windows.go new file mode 100644 index 00000000000..43c3c49a36a --- /dev/null +++ b/pkg/agent/wireguard/client_windows.go @@ -0,0 +1,28 @@ +// +build windows + +// Copyright 2021 Antrea 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 wireguard + +import ( + "fmt" + + clientset "k8s.io/client-go/kubernetes" + + "antrea.io/antrea/pkg/agent/config" +) + +func New(clientSet clientset.Interface, nodeConfig *config.NodeConfig, wireGuardConfig *config.WireGuardConfig) (Interface, error) { + return nil, fmt.Errorf("WireGuard is not implemented for windows") +} diff --git a/pkg/agent/wireguard/interface.go b/pkg/agent/wireguard/interface.go new file mode 100644 index 00000000000..f548dd36b31 --- /dev/null +++ b/pkg/agent/wireguard/interface.go @@ -0,0 +1,33 @@ +// Copyright 2021 Antrea 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 wireguard + +import ( + "net" +) + +type Interface interface { + // Init initializes the WireGuard client and sets up the WireGuard device. + // It will generate a new private key if necessary and update the public key to the Node's annotation. + Init() error + // UpdatePeer updates WireGuard peer by provided public key and Node IPs. + // It will create a new WireGuard peer if the specified Node is not present in WireGuard device. + UpdatePeer(nodeName, publicKeyString string, peerNodeIP net.IP, podCIDRs []*net.IPNet) error + // RemoveStalePeers reads existing WireGuard peers from the WireGuard device and deletes those which are not in currentPeerPublickeys. + // It is useful to clean up stale WireGuard peers upon antrea starting. + RemoveStalePeers(currentPeerPublickeys []string) error + // DeletePeer deletes the WireGuard peer by Node name. + DeletePeer(nodeName string) error +} diff --git a/pkg/apis/ports.go b/pkg/apis/ports.go index c22a0bd5a43..e0c2a53c3ae 100644 --- a/pkg/apis/ports.go +++ b/pkg/apis/ports.go @@ -22,4 +22,6 @@ const ( // AntreaAgentClusterMembershipPort is the default port for the antrea-agent cluster. // A gossip-based cluster will be created in the background when the egress feature is turned on. AntreaAgentClusterMembershipPort = 10351 + // WireGuardListenPort is the default port for WireGuard encrypted traffic. + WireGuardListenPort = 51850 ) diff --git a/plugins/octant/go.sum b/plugins/octant/go.sum index 5aa3133e9b0..499b95a8960 100644 --- a/plugins/octant/go.sum +++ b/plugins/octant/go.sum @@ -336,6 +336,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -426,8 +427,14 @@ github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6t github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 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/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.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= @@ -483,12 +490,21 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= 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/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= +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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -658,8 +674,10 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20210510164352-d17758a128bf/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware-tanzu/octant v0.17.0 h1:2H2AiQU5C1RiHxxYrrOosDHHI9eV51nP+e9PjRP+c48= github.com/vmware-tanzu/octant v0.17.0/go.mod h1:lA32xKa6icUclg+DjAX/E/Id1cTqwCXZUem3RGEp/2A= github.com/vmware/go-ipfix v0.5.7/go.mod h1:yzbG1rv+yJ8GeMrRm+MDhOV3akygNZUHLhC1pDoD2AY= @@ -718,8 +736,9 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI= +golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -796,12 +815,18 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/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-20201024042810-be3efd7ff127/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-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +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-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM= +golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -862,6 +887,7 @@ golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -872,15 +898,26 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/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-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-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-20210309040221-94ec62e08169/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-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -892,8 +929,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -966,6 +1004,8 @@ 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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/test/e2e/connectivity_test.go b/test/e2e/connectivity_test.go index d393c406666..3eecd06347f 100644 --- a/test/e2e/connectivity_test.go +++ b/test/e2e/connectivity_test.go @@ -197,7 +197,7 @@ func createPodsOnDifferentNodes(t *testing.T, data *TestData, tag string) (podIn for _, p := range pods.Items { os := clusterInfo.nodesOS[p.Spec.NodeName] - piMap[os] = append(piMap[os], podInfo{p.Name, os}) + piMap[os] = append(piMap[os], podInfo{p.Name, os, p.Spec.NodeName}) } var linIdx, winIdx int for linIdx != len(piMap["linux"]) && winIdx != len(piMap["windows"]) { @@ -242,29 +242,19 @@ func testPodConnectivityDifferentNodes(t *testing.T, data *TestData) { data.testPodConnectivityDifferentNodes(t) } -func (data *TestData) redeployAntrea(t *testing.T, enableIPSec bool) { +func (data *TestData) redeployAntrea(t *testing.T, option deployAntreaOptions) { var err error // export logs before deleting Antrea - if enableIPSec { - exportLogs(t, data, "beforeRedeployWithIPsec", false) - } else { - exportLogs(t, data, "beforeRedploy", false) - } + exportLogs(t, data, fmt.Sprintf("beforeRedeploy%s", option), false) t.Logf("Deleting Antrea Agent DaemonSet") if err = data.deleteAntrea(defaultTimeout); err != nil { t.Fatalf("Error when deleting Antrea DaemonSet: %v", err) } - t.Logf("Applying Antrea YAML") - if enableIPSec { - err = data.deployAntreaIPSec() - } else { - err = data.deployAntrea() - } + err = data.deployAntrea(option) if err != nil { t.Fatalf("Error when applying Antrea YAML: %v", err) } - // After redeploying Antrea with / without IPsec, we wait for watchForRestartsDuration and // count the number of container restarts. watchForRestartsDuration should be large enough // to detect issues, e.g. if there is an issue with the antrea-ipsec container. @@ -302,7 +292,7 @@ func testPodConnectivityAfterAntreaRestart(t *testing.T, data *TestData) { data.runPingMesh(t, podInfos[:numPods], agnhostContainerName) - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployAntreaDefault) data.runPingMesh(t, podInfos[:numPods], agnhostContainerName) } diff --git a/test/e2e/fixtures.go b/test/e2e/fixtures.go index cc183ba9ec5..0dbb1373010 100644 --- a/test/e2e/fixtures.go +++ b/test/e2e/fixtures.go @@ -136,7 +136,7 @@ func skipIfFeatureDisabled(tb testing.TB, feature featuregate.Feature, checkAgen func ensureAntreaRunning(data *TestData) error { log.Println("Applying Antrea YAML") - if err := data.deployAntrea(); err != nil { + if err := data.deployAntrea(deployAntreaDefault); err != nil { return err } log.Println("Waiting for all Antrea DaemonSet Pods") diff --git a/test/e2e/framework.go b/test/e2e/framework.go index 1e118e7acd5..2d751fa56ff 100644 --- a/test/e2e/framework.go +++ b/test/e2e/framework.go @@ -80,6 +80,8 @@ const ( agentContainerName string = "antrea-agent" antreaYML string = "antrea.yml" antreaIPSecYML string = "antrea-ipsec.yml" + antreaWireGuardGoYML string = "antrea-wireguard-go.yml" + antreaWireGuardGoCovYML string = "antrea-wireguard-go-coverage.yml" antreaCovYML string = "antrea-coverage.yml" antreaIPSecCovYML string = "antrea-ipsec-coverage.yml" flowAggregatorYML string = "flow-aggregator.yml" @@ -161,8 +163,9 @@ var provider providers.ProviderInterface // podInfo combines OS info with a Pod name. It is useful when choosing commands and options on Pods of different OS (Windows, Linux). type podInfo struct { - name string - os string + name string + os string + nodeName string } // TestData stores the state required for each test case. @@ -189,6 +192,43 @@ type PodIPs struct { ipStrings []string } +type deployAntreaOptions int + +const ( + deployAntreaDefault deployAntreaOptions = iota + deployAntreaIPsec + deployAntreaWireGuardGo + deployAntreaCoverageOffset +) + +func (o deployAntreaOptions) WithCoverage() deployAntreaOptions { + return o + deployAntreaCoverageOffset +} + +func (o deployAntreaOptions) DeployYml() string { + return deployAntreaOptionsYml[o] +} + +func (o deployAntreaOptions) String() string { + return deployAntreaOptionsString[o] +} + +var ( + deployAntreaOptionsString = [...]string{ + "AntreaDefault", + "AntreaWithIPSec", + "AntreaWithWireGuardGo", + } + deployAntreaOptionsYml = [...]string{ + antreaYML, + antreaIPSecYML, + antreaWireGuardGoYML, + antreaCovYML, + antreaIPSecCovYML, + antreaWireGuardGoCovYML, + } +) + func (p PodIPs) String() string { res := "" if p.ipv4 != nil { @@ -562,20 +602,12 @@ func (data *TestData) deployAntreaCommon(yamlFile string, extraOptions string, w return nil } -// deployAntrea deploys Antrea with the standard manifest. -func (data *TestData) deployAntrea() error { - if testOptions.enableCoverage { - return data.deployAntreaCommon(antreaCovYML, "", true) - } - return data.deployAntreaCommon(antreaYML, "", true) -} - -// deployAntreaIPSec deploys Antrea with IPSec tunnel enabled. -func (data *TestData) deployAntreaIPSec() error { +// deployAntrea deploys Antrea with deploy options. +func (data *TestData) deployAntrea(option deployAntreaOptions) error { if testOptions.enableCoverage { - return data.deployAntreaCommon(antreaIPSecCovYML, "", true) + option = option.WithCoverage() } - return data.deployAntreaCommon(antreaIPSecYML, "", true) + return data.deployAntreaCommon(option.DeployYml(), "", true) } // deployAntreaFlowExporter deploys Antrea with flow exporter config params enabled. diff --git a/test/e2e/ipsec_test.go b/test/e2e/ipsec_test.go index cb2c1b3d429..9083f810bb3 100644 --- a/test/e2e/ipsec_test.go +++ b/test/e2e/ipsec_test.go @@ -76,7 +76,7 @@ func (data *TestData) readSecurityAssociationsStatus(nodeName string) (up int, c // them ping each other. func testIPSecTunnelConnectivity(t *testing.T, data *TestData) { t.Logf("Redeploy Antrea with IPSec tunnel enabled") - data.redeployAntrea(t, true) + data.redeployAntrea(t, deployAntreaIPsec) data.testPodConnectivityDifferentNodes(t) @@ -92,7 +92,7 @@ func testIPSecTunnelConnectivity(t *testing.T, data *TestData) { } // Restore normal Antrea deployment with IPSec disabled. - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployAntreaDefault) } // testIPSecDeleteStaleTunnelPorts checks that when switching from IPsec mode to @@ -100,7 +100,7 @@ func testIPSecTunnelConnectivity(t *testing.T, data *TestData) { // correctly. func testIPSecDeleteStaleTunnelPorts(t *testing.T, data *TestData) { t.Logf("Redeploy Antrea with IPSec tunnel enabled") - data.redeployAntrea(t, true) + data.redeployAntrea(t, deployAntreaIPsec) nodeName0 := nodeName(0) nodeName1 := nodeName(1) @@ -132,7 +132,7 @@ func testIPSecDeleteStaleTunnelPorts(t *testing.T, data *TestData) { } t.Logf("Redeploy Antrea with IPSec tunnel disabled") - data.redeployAntrea(t, false) + data.redeployAntrea(t, deployAntreaDefault) t.Logf("Checking that tunnel port has been deleted") if err := wait.PollImmediate(defaultInterval, defaultTimeout, func() (found bool, err error) { diff --git a/test/e2e/performance_test.go b/test/e2e/performance_test.go index 9182a383a9e..f2f1033cc0c 100644 --- a/test/e2e/performance_test.go +++ b/test/e2e/performance_test.go @@ -320,7 +320,7 @@ func withPerfTestSetup(fn func(data *TestData), b *testing.B) { b.Fatalf("Error when deleting Antrea DaemonSet: %v", err) } b.Logf("Applying Antrea YAML") - if err := data.deployAntrea(); err != nil { + if err := data.deployAntrea(deployAntreaDefault); err != nil { b.Fatalf("Error when restarting Antrea: %v", err) } b.Logf("Waiting for all Antrea DaemonSet Pods") diff --git a/test/e2e/traceflow_test.go b/test/e2e/traceflow_test.go index da0f4032c52..a4f3b8653bb 100644 --- a/test/e2e/traceflow_test.go +++ b/test/e2e/traceflow_test.go @@ -2111,13 +2111,13 @@ func runTestTraceflow(t *testing.T, data *TestData, tc testcase) { } } else { dstPod := tc.tf.Spec.Destination.Pod - podIPs := waitForPodIPs(t, data, []podInfo{{dstPod, "linux"}}) + podIPs := waitForPodIPs(t, data, []podInfo{{dstPod, "linux", ""}}) dstPodIPs = podIPs[dstPod] } // Give a little time for Nodes to install OVS flows. time.Sleep(time.Second * 2) // Send an ICMP echo packet from the source Pod to the destination. - if err := data.runPingCommandFromTestPod(podInfo{srcPod, "linux"}, testNamespace, dstPodIPs, busyboxContainerName, 2, 0); err != nil { + if err := data.runPingCommandFromTestPod(podInfo{srcPod, "linux", ""}, testNamespace, dstPodIPs, busyboxContainerName, 2, 0); err != nil { t.Logf("Ping '%s' -> '%v' failed: ERROR (%v)", srcPod, *dstPodIPs, err) } } diff --git a/test/e2e/wireguard_test.go b/test/e2e/wireguard_test.go new file mode 100644 index 00000000000..ef0a9cb5f12 --- /dev/null +++ b/test/e2e/wireguard_test.go @@ -0,0 +1,117 @@ +// Copyright 2021 Antrea 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 e2e + +import ( + "fmt" + "strings" + "testing" +) + +// TestWireGuardTunnelConnectivity checks that Pod traffic across two Nodes over +// the WireGuard tunnel, by creating multiple Pods across distinct Nodes and having +// them ping each other. +func TestWireGuard(t *testing.T) { + skipIfNumNodesLessThan(t, 2) + skipIfHasWindowsNodes(t) + providerIsKind := testOptions.providerName == "kind" + + if !providerIsKind { + for _, node := range clusterInfo.nodes { + skipIfMissingKernelModule(t, node.name, []string{"wireguard"}) + } + } + data, err := setupTest(t) + if err != nil { + t.Fatalf("Error when setting up test: %v", err) + } + defer teardownTest(t, data) + + if !providerIsKind { + ac := []configChange{ + {"trafficEncryptionMode", "wireguard", false}, + } + if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil { + t.Fatalf("Failed to enable WireGuard tunnel: %v", err) + } + defer func() { + ac = []configChange{ + {"trafficEncryptionMode", "none", false}, + } + if err := data.mutateAntreaConfigMap(nil, ac, false, true); err != nil { + t.Fatalf("Failed to disable WireGuard tunnel: %v", err) + } + }() + } else { + data.redeployAntrea(t, deployAntreaWireGuardGo) + defer data.redeployAntrea(t, deployAntreaDefault) + } + + t.Run("testWireGuardTunnelConnectivity", func(t *testing.T) { testWireGuardTunnelConnectivity(t, data) }) +} + +func (data *TestData) getWireGuardPeersWithLastHandshake(nodeName string) ([]string, error) { + var peerPublickeys []string + antreaPodName, err := data.getAntreaPodOnNode(nodeName) + if err != nil { + return peerPublickeys, err + } + cmd := []string{"wg"} + stdout, stderr, err := data.runCommandFromPod(antreaNamespace, antreaPodName, "wireguard", cmd) + if err != nil { + return peerPublickeys, fmt.Errorf("error when running 'wg' on '%s': %v - stdout: %s - stderr: %s", nodeName, err, stdout, stderr) + } + peerConfigs := strings.Split(stdout, "\n\n") + if len(peerConfigs) < 1 { + return peerPublickeys, fmt.Errorf("invalid 'wg' output on '%s': %v - stdout: %s - stderr: %s", nodeName, err, stdout, stderr) + } + for _, p := range peerConfigs[1:] { + lines := strings.Split(p, "\n") + if len(lines) < 1 { + return peerPublickeys, fmt.Errorf("invalid WireGuard peer config output - %s", p) + } + peerPublicKey := strings.TrimPrefix(lines[0], "peer: ") + if len(peerPublicKey) == 0 { + return peerPublickeys, fmt.Errorf("invalid WireGuard peer config output - %s", p) + } + for _, l := range lines { + if strings.Contains(l, "latest handshake") { + peerPublickeys = append(peerPublickeys, peerPublicKey) + break + } + } + } + return peerPublickeys, nil +} + +func testWireGuardTunnelConnectivity(t *testing.T, data *TestData) { + podInfos, deletePods := createPodsOnDifferentNodes(t, data, "differentnodes") + defer deletePods() + numPods := 2 + data.runPingMesh(t, podInfos[:numPods], agnhostContainerName) + // wg command is only available in WireGuard sidecar container. + if testOptions.providerName == "kind" { + nodeName := podInfos[0].nodeName + peersWithHandshake, err := data.getWireGuardPeersWithLastHandshake(nodeName) + if err != nil { + t.Errorf("Error when getting WireGuard peers with handshake established: %v", err) + } + if len(peersWithHandshake) == 0 { + t.Errorf("Could not find WireGuard peers with handshake established") + } else { + t.Logf("Found peers %v with handshake established for Node '%s'", peersWithHandshake, nodeName) + } + } +} diff --git a/test/integration/agent/openflow_test.go b/test/integration/agent/openflow_test.go index b44329c6b25..44214c005c3 100644 --- a/test/integration/agent/openflow_test.go +++ b/test/integration/agent/openflow_test.go @@ -168,7 +168,7 @@ func TestReplayFlowsNetworkPolicyFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge: %v", err)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -255,7 +255,7 @@ func testReplayFlows(t *testing.T) { } func testInitialize(t *testing.T, config *testConfig) { - if _, err := c.Initialize(roundInfo, config.nodeConfig, config1.TrafficEncapModeEncap); err != nil { + if _, err := c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}); err != nil { t.Errorf("Failed to initialize openflow client: %v", err) } for _, tableFlow := range prepareDefaultFlows(config) { @@ -347,7 +347,7 @@ func TestNetworkPolicyFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{PodIPv4CIDR: podIPv4CIDR, PodIPv6CIDR: podIPv6CIDR}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -489,7 +489,7 @@ func TestProxyServiceFlows(t *testing.T) { err := ofTestUtils.PrepareOVSBridge(br) require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) - _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, &config1.NodeConfig{}, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() { @@ -1363,7 +1363,7 @@ func TestSNATFlows(t *testing.T) { require.Nil(t, err, fmt.Sprintf("Failed to prepare OVS bridge %s", br)) config := prepareConfiguration() - _, err = c.Initialize(roundInfo, config.nodeConfig, config1.TrafficEncapModeEncap) + _, err = c.Initialize(roundInfo, config.nodeConfig, &config1.NetworkConfig{TrafficEncapMode: config1.TrafficEncapModeEncap}) require.Nil(t, err, "Failed to initialize OFClient") defer func() {