Skip to content

Commit 43d2c68

Browse files
authored
Setup SNAT Configuration Based on Azure Host Support (#401)
* Save enable snat on host settings after querying NMagent version * Adding changes to exclude outbound snat for win cni if new NMAgent is running * try to acquire lock file when writing to disableSnatOnHost.json * addressed some of Tamilmani's comments * Adding snat for DNS if current NMAgent does not support it yet * Adding DNS NAT changes for Windows CNI * vendoring HCSShim changes that support destination based SNATing * Reverting k8s.io/api dependencies from master branch to last working version * Addressing Tamilmani's comments * syncing with an older version of k8s.io dependencies * verify valid windows version before Dns NAT. * only remove snat on windows when host has full support * addressing Tamilmani's comments * addressing Tamilmani's comments * rebased and re-depped
1 parent 2c0ae6d commit 43d2c68

File tree

747 files changed

+41340
-26239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

747 files changed

+41340
-26239
lines changed

Gopkg.lock

Lines changed: 133 additions & 98 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
[[constraint]]
2929
name = "github.com/Microsoft/go-winio"
30-
version = "0.4.12"
30+
version = "0.4.14"
3131

3232
[[constraint]]
3333
name = "github.com/docker/libnetwork"
@@ -42,20 +42,20 @@
4242
name = "golang.org/x/sys"
4343

4444
[[constraint]]
45-
branch = "master"
4645
name = "k8s.io/api"
46+
version = "kubernetes-1.13.6"
4747

4848
[[constraint]]
4949
name = "k8s.io/apimachinery"
50-
revision = "d7deff9243b165ee192f5551710ea4285dcfd615"
50+
revision = "kubernetes-1.13.6"
5151

5252
[[constraint]]
5353
name = "k8s.io/client-go"
54-
version = "11.0.0"
54+
version = "kubernetes-1.13.6"
5555

5656
[[constraint]]
5757
name = "github.com/Microsoft/hcsshim"
58-
version = "0.8.6"
58+
revision = "2a08d6fcd23883573a39ba32ab7ed2b197a9a9eb"
5959

6060
[[constraint]]
6161
name = "github.com/containernetworking/cni"

cni/network/multitenancy.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func SetupRoutingForMultitenancy(
2626
result *cniTypesCurr.Result) {
2727
// Adding default gateway
2828
if nwCfg.MultiTenancy {
29-
// if snat enabled, add 169.254.0.1 as default gateway
29+
// if snat enabled, add 169.254.128.1 as default gateway
3030
if nwCfg.EnableSnatOnHost {
3131
log.Printf("add default route for multitenancy.snat on host enabled")
3232
addDefaultRoute(cnsNetworkConfig.LocalIPConfiguration.GatewayIPAddress, epInfo, result)
@@ -36,6 +36,11 @@ func SetupRoutingForMultitenancy(
3636
gwIP := net.ParseIP(cnsNetworkConfig.IPConfiguration.GatewayIPAddress)
3737
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: dstIP, Gw: gwIP})
3838
result.Routes = append(result.Routes, &cniTypes.Route{Dst: dstIP, GW: gwIP})
39+
40+
if epInfo.EnableSnatForDns {
41+
log.Printf("add SNAT for DNS enabled")
42+
addSnatForDNS(cnsNetworkConfig.LocalIPConfiguration.GatewayIPAddress, epInfo, result)
43+
}
3944
}
4045

4146
setupInfraVnetRoutingForMultitenancy(nwCfg, azIpamResult, epInfo, result)

cni/network/network.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ package network
66
import (
77
"encoding/json"
88
"fmt"
9+
"io/ioutil"
910
"net"
11+
"net/http"
12+
"os"
13+
"strings"
14+
"time"
1015

1116
"github.com/Azure/azure-container-networking/cni"
1217
"github.com/Azure/azure-container-networking/cns"
@@ -35,13 +40,34 @@ const (
3540
CNI_UPDATE = "UPDATE"
3641
)
3742

43+
const (
44+
// URL to query NMAgent version and determine whether we snat on host
45+
nmAgentSupportedApisURL = "http://168.63.129.16/machine/plugins/?comp=nmagent&type=GetSupportedApis"
46+
// Only SNAT support (no DNS support)
47+
nmAgentSnatSupportAPI = "NetworkManagementSnatSupport"
48+
// SNAT and DNS are both supported
49+
nmAgentSnatAndDnsSupportAPI = "NetworkManagementDNSSupport"
50+
)
51+
52+
// temporary consts related func determineSnat() which is to be deleted after
53+
// a baking period with newest NMAgent changes
54+
const (
55+
jsonFileExtension = ".json"
56+
)
57+
3858
// NetPlugin represents the CNI network plugin.
3959
type netPlugin struct {
4060
*cni.Plugin
4161
nm network.NetworkManager
4262
report *telemetry.CNIReport
4363
}
4464

65+
// snatConfiguration contains a bool that determines whether CNI enables snat on host and snat for dns
66+
type snatConfiguration struct {
67+
EnableSnatOnHost bool
68+
EnableSnatForDns bool
69+
}
70+
4571
// NewPlugin creates a new netPlugin object.
4672
func NewPlugin(name string, config *common.PluginConfig) (*netPlugin, error) {
4773
// Setup base plugin.
@@ -190,6 +216,7 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
190216
subnetPrefix net.IPNet
191217
cnsNetworkConfig *cns.GetNetworkContainerResponse
192218
enableInfraVnet bool
219+
enableSnatForDns bool
193220
nwDNSInfo network.DNSInfo
194221
)
195222

@@ -205,6 +232,13 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
205232

206233
log.Printf("[cni-net] Read network configuration %+v.", nwCfg)
207234

235+
// Temporary if block to determing whether we disable SNAT on host (for multi-tenant scenario only)
236+
if nwCfg.MultiTenancy {
237+
if enableSnatForDns, nwCfg.EnableSnatOnHost, err = determineSnat(); err != nil {
238+
return err
239+
}
240+
}
241+
208242
plugin.setCNIReportDetails(nwCfg, CNI_ADD, "")
209243

210244
defer func() {
@@ -453,6 +487,7 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
453487
EnableSnatOnHost: nwCfg.EnableSnatOnHost,
454488
EnableMultiTenancy: nwCfg.MultiTenancy,
455489
EnableInfraVnet: enableInfraVnet,
490+
EnableSnatForDns: enableSnatForDns,
456491
PODName: k8sPodName,
457492
PODNameSpace: k8sNamespace,
458493
SkipHotAttachEp: false, // Hot attach at the time of endpoint creation
@@ -855,3 +890,68 @@ func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error {
855890

856891
return nil
857892
}
893+
894+
// Temporary function to determine whether we need to disable SNAT due to NMAgent support
895+
func determineSnat() (bool, bool, error) {
896+
var (
897+
snatConfig snatConfiguration
898+
retrieveSnatConfigErr error
899+
jsonFile *os.File
900+
httpClient = &http.Client{Timeout: time.Second * 5}
901+
snatConfigFile = snatConfigFileName + jsonFileExtension
902+
)
903+
904+
// Check if we've already retrieved NMAgent version and determined whether to disable snat on host
905+
if jsonFile, retrieveSnatConfigErr = os.Open(snatConfigFile); retrieveSnatConfigErr == nil {
906+
bytes, _ := ioutil.ReadAll(jsonFile)
907+
jsonFile.Close()
908+
if retrieveSnatConfigErr = json.Unmarshal(bytes, &snatConfig); retrieveSnatConfigErr != nil {
909+
log.Errorf("[cni-net] failed to unmarshal to snatConfig with error %v",
910+
retrieveSnatConfigErr)
911+
}
912+
913+
}
914+
915+
// If we weren't able to retrieve snatConfiguration, query NMAgent
916+
if retrieveSnatConfigErr != nil {
917+
var resp *http.Response
918+
resp, retrieveSnatConfigErr = httpClient.Get(nmAgentSupportedApisURL)
919+
if retrieveSnatConfigErr == nil {
920+
defer resp.Body.Close()
921+
922+
if resp.StatusCode == http.StatusOK {
923+
var bodyBytes []byte
924+
// if the list of APIs (strings) contains the nmAgentSnatSupportAPI we will disable snat on host
925+
if bodyBytes, retrieveSnatConfigErr = ioutil.ReadAll(resp.Body); retrieveSnatConfigErr == nil {
926+
bodyStr := string(bodyBytes)
927+
if !strings.Contains(bodyStr, nmAgentSnatAndDnsSupportAPI) {
928+
snatConfig.EnableSnatForDns = true
929+
snatConfig.EnableSnatOnHost = !strings.Contains(bodyStr, nmAgentSnatSupportAPI)
930+
}
931+
932+
jsonStr, _ := json.Marshal(snatConfig)
933+
fp, err := os.OpenFile(snatConfigFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0664))
934+
if err == nil {
935+
fp.Write(jsonStr)
936+
fp.Close()
937+
} else {
938+
log.Printf("[cni-net] failed to save snatConfig")
939+
}
940+
}
941+
} else {
942+
retrieveSnatConfigErr = fmt.Errorf("nmagent request status code %d", resp.StatusCode)
943+
}
944+
}
945+
}
946+
947+
// Log and return the error when we fail acquire snat configuration for host and dns
948+
if retrieveSnatConfigErr != nil {
949+
log.Errorf("[cni-net] failed to acquire SNAT configuration with error %v",
950+
retrieveSnatConfigErr)
951+
return snatConfig.EnableSnatForDns, snatConfig.EnableSnatOnHost, retrieveSnatConfigErr
952+
}
953+
954+
log.Printf("[cni-net] EnableSnatOnHost set to %t; EnableSnatForDns set to %t", snatConfig.EnableSnatOnHost, snatConfig.EnableSnatForDns)
955+
956+
return snatConfig.EnableSnatForDns, snatConfig.EnableSnatOnHost, nil
957+
}

cni/network/network_linux.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const (
2020
infraInterface = "eth2"
2121
)
2222

23+
const (
24+
snatConfigFileName = "/tmp/snatConfig"
25+
)
26+
2327
// handleConsecutiveAdd is a dummy function for Linux platform.
2428
func handleConsecutiveAdd(args *cniSkel.CmdArgs, endpointId string, nwInfo *network.NetworkInfo, nwCfg *cni.NetworkConfig) (*cniTypesCurr.Result, error) {
2529
return nil, nil
@@ -33,6 +37,13 @@ func addDefaultRoute(gwIPString string, epInfo *network.EndpointInfo, result *cn
3337
result.Routes = append(result.Routes, &cniTypes.Route{Dst: dstIP, GW: gwIP})
3438
}
3539

40+
func addSnatForDNS(gwIPString string, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
41+
_, dnsIPNet, _ := net.ParseCIDR("168.63.129.16/32")
42+
gwIP := net.ParseIP(gwIPString)
43+
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: *dnsIPNet, Gw: gwIP, DevName: snatInterface})
44+
result.Routes = append(result.Routes, &cniTypes.Route{Dst: *dnsIPNet, GW: gwIP})
45+
}
46+
3647
func addInfraRoutes(azIpamResult *cniTypesCurr.Result, result *cniTypesCurr.Result, epInfo *network.EndpointInfo) {
3748
for _, route := range azIpamResult.Routes {
3849
epInfo.Routes = append(epInfo.Routes, network.RouteInfo{Dst: route.Dst, Gw: route.GW, DevName: infraInterface})

cni/network/network_windows.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"encoding/json"
55
"fmt"
66
"net"
7+
"os"
8+
"path/filepath"
79
"strconv"
810
"strings"
911

@@ -13,12 +15,19 @@ import (
1315
"github.com/Azure/azure-container-networking/network"
1416
"github.com/Azure/azure-container-networking/network/policy"
1517
"github.com/Microsoft/hcsshim"
18+
"golang.org/x/sys/windows/registry"
1619

1720
cniSkel "github.com/containernetworking/cni/pkg/skel"
1821
cniTypes "github.com/containernetworking/cni/pkg/types"
1922
cniTypesCurr "github.com/containernetworking/cni/pkg/types/current"
2023
)
2124

25+
var (
26+
snatConfigFileName = filepath.FromSlash(os.Getenv("TEMP")) + "\\snatConfig"
27+
// windows build for version 1903
28+
win1903Version = 18362
29+
)
30+
2231
/* handleConsecutiveAdd handles consecutive add calls for infrastructure containers on Windows platform.
2332
* This is a temporary work around for issue #57253 of Kubernetes.
2433
* We can delete this if statement once they fix it.
@@ -73,6 +82,9 @@ func handleConsecutiveAdd(args *cniSkel.CmdArgs, endpointId string, nwInfo *netw
7382
func addDefaultRoute(gwIPString string, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
7483
}
7584

85+
func addSnatForDNS(gwIPString string, epInfo *network.EndpointInfo, result *cniTypesCurr.Result) {
86+
}
87+
7688
func addInfraRoutes(azIpamResult *cniTypesCurr.Result, result *cniTypesCurr.Result, epInfo *network.EndpointInfo) {
7789
}
7890

@@ -125,6 +137,7 @@ func getNetworkName(podName, podNs, ifName string, nwCfg *cni.NetworkConfig) (ne
125137
networkName = nwCfg.Name
126138
err = nil
127139
if nwCfg.MultiTenancy {
140+
determineWinVer()
128141
if len(strings.TrimSpace(podName)) == 0 || len(strings.TrimSpace(podNs)) == 0 {
129142
err = fmt.Errorf("POD info cannot be empty. PodName: %s, PodNamespace: %s", podName, podNs)
130143
return
@@ -242,3 +255,22 @@ func getCustomDNS(nwCfg *cni.NetworkConfig) network.DNSInfo {
242255
Options: nwCfg.RuntimeConfig.DNS.Options,
243256
}
244257
}
258+
259+
func determineWinVer() {
260+
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
261+
if err == nil {
262+
defer k.Close()
263+
264+
cb, _, err := k.GetStringValue("CurrentBuild")
265+
if err == nil {
266+
winVer, err := strconv.Atoi(cb)
267+
if err == nil {
268+
policy.ValidWinVerForDnsNat = winVer >= win1903Version
269+
}
270+
}
271+
}
272+
273+
if err != nil {
274+
log.Errorf(err.Error())
275+
}
276+
}

network/endpoint.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type EndpointInfo struct {
6262
EnableSnatOnHost bool
6363
EnableInfraVnet bool
6464
EnableMultiTenancy bool
65+
EnableSnatForDns bool
6566
AllowInboundFromHostToNC bool
6667
AllowInboundFromNCToHost bool
6768
NetworkContainerID string

network/endpoint_windows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func (nw *network) newEndpointImplHnsV1(epInfo *EndpointInfo) (*endpoint, error)
8787
VirtualNetwork: nw.HnsId,
8888
DNSSuffix: epInfo.DNS.Suffix,
8989
DNSServerList: strings.Join(epInfo.DNS.Servers, ","),
90-
Policies: policy.SerializePolicies(policy.EndpointPolicy, epInfo.Policies, epInfo.Data),
90+
Policies: policy.SerializePolicies(policy.EndpointPolicy, epInfo.Policies, epInfo.Data, epInfo.EnableSnatForDns, epInfo.EnableMultiTenancy),
9191
}
9292

9393
// HNS currently supports only one IP address per endpoint.
@@ -175,7 +175,7 @@ func (nw *network) configureHcnEndpoint(epInfo *EndpointInfo) (*hcn.HostComputeE
175175
MacAddress: epInfo.MacAddress.String(),
176176
}
177177

178-
if endpointPolicies, err := policy.GetHcnEndpointPolicies(policy.EndpointPolicy, epInfo.Policies, epInfo.Data); err == nil {
178+
if endpointPolicies, err := policy.GetHcnEndpointPolicies(policy.EndpointPolicy, epInfo.Policies, epInfo.Data, epInfo.EnableSnatForDns, epInfo.EnableMultiTenancy); err == nil {
179179
for _, epPolicy := range endpointPolicies {
180180
hcnEndpoint.Policies = append(hcnEndpoint.Policies, epPolicy)
181181
}

network/network_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (nm *networkManager) newNetworkImplHnsV1(nwInfo *NetworkInfo, extIf *extern
6161
Name: nwInfo.Id,
6262
NetworkAdapterName: networkAdapterName,
6363
DNSServerList: strings.Join(nwInfo.DNS.Servers, ","),
64-
Policies: policy.SerializePolicies(policy.NetworkPolicy, nwInfo.Policies, nil),
64+
Policies: policy.SerializePolicies(policy.NetworkPolicy, nwInfo.Policies, nil, false, false),
6565
}
6666

6767
// Set the VLAN and OutboundNAT policies

network/ovs_endpoint_snatroute_linux.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import (
88
)
99

1010
func NewSnatClient(client *OVSEndpointClient, snatBridgeIP string, localIP string, epInfo *EndpointInfo) {
11-
if client.enableSnatOnHost || client.allowInboundFromHostToNC {
12-
11+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
1312
hostIfName := fmt.Sprintf("%s%s", snatVethInterfacePrefix, epInfo.Id[:7])
1413
contIfName := fmt.Sprintf("%s%s-2", snatVethInterfacePrefix, epInfo.Id[:7])
1514

@@ -18,7 +17,7 @@ func NewSnatClient(client *OVSEndpointClient, snatBridgeIP string, localIP strin
1817
}
1918

2019
func AddSnatEndpoint(client *OVSEndpointClient) error {
21-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
20+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
2221
if err := client.snatClient.CreateSnatEndpoint(client.bridgeName); err != nil {
2322
return err
2423
}
@@ -28,7 +27,7 @@ func AddSnatEndpoint(client *OVSEndpointClient) error {
2827
}
2928

3029
func AddSnatEndpointRules(client *OVSEndpointClient) error {
31-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
30+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
3231
// Allow specific Private IPs via Snat Bridge
3332
if err := client.snatClient.AllowIPAddressesOnSnatBrdige(); err != nil {
3433
return err
@@ -63,31 +62,31 @@ func AddSnatEndpointRules(client *OVSEndpointClient) error {
6362
}
6463

6564
func MoveSnatEndpointToContainerNS(client *OVSEndpointClient, netnsPath string, nsID uintptr) error {
66-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
65+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
6766
return client.snatClient.MoveSnatEndpointToContainerNS(netnsPath, nsID)
6867
}
6968

7069
return nil
7170
}
7271

7372
func SetupSnatContainerInterface(client *OVSEndpointClient) error {
74-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
73+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
7574
return client.snatClient.SetupSnatContainerInterface()
7675
}
7776

7877
return nil
7978
}
8079

8180
func ConfigureSnatContainerInterface(client *OVSEndpointClient) error {
82-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
81+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
8382
return client.snatClient.ConfigureSnatContainerInterface()
8483
}
8584

8685
return nil
8786
}
8887

8988
func DeleteSnatEndpoint(client *OVSEndpointClient) error {
90-
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost {
89+
if client.enableSnatOnHost || client.allowInboundFromHostToNC || client.allowInboundFromNCToHost || client.enableSnatForDns {
9190
return client.snatClient.DeleteSnatEndpoint()
9291
}
9392

0 commit comments

Comments
 (0)